ดึงข้อมูลประเภทเทมเพลตด้านในสุดภายในเทมเพลตนั้นเอง

เป็นไปได้หรือไม่ที่จะดึงเทมเพลตที่ซ้อนกันประเภทชั้นในสุดประเภทเดียวกัน จากภายในเทมเพลต ฉันต้องการดึงข้อมูลประเภท double ในตัวอย่างต่อไปนี้:

template<typename T>
struct is_a : std::false_type {};

template<typename T>
struct A
{
    using type = std::conditional_t<
        is_a<T>::value,
        T::type, // if it's an A, go deeper
        T>;      // if not, we're done
};
template<typename T>
struct is_a<A<T>> : std::true_type {};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}

คำถามได้รับแรงบันดาลใจจากคำถามนี้ นอกจากนี้ ฉันยังพบโพสต์นี้ ซึ่งบ่งชี้ว่าอาจมีบางอย่างเกี่ยวข้องกับการวางคำหลัก typename หรือ template แต่ฉันไม่สามารถทำงานด้วยตัวเองได้


person Thomas B.    schedule 15.05.2018    source แหล่งที่มา


คำตอบ (5)


เคล็ดลับปกติในการดำเนินการตามแนวทางเดิมของคุณคือการเลื่อนการประเมินออกไป:

template<class T> struct type_identity { using type = T; };

template<typename T>
struct A
{
    using type = typename std::conditional_t<
        is_a<T>::value,
        T,
        type_identity<T>>::type;
};
person T.C.    schedule 15.05.2018
comment
ก็ต้องยอมรับข้อนี้เพราะมันใกล้เคียงกับคำถามเดิมมากกว่า ตอนนี้เรามีทั้งสองแนวทางตรงนี้ นี่เป็นสิ่งที่ดี - person Thomas B.; 15.05.2018

ยกเว้นในกรณีที่ฉันขาดอะไรบางอย่างไป ฉันแค่เชี่ยวชาญเทมเพลตบางส่วนเพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้น

template<typename T>
struct A
{
    using type = T;
};

template<typename T>
struct A<A<T>>
{
    using type = typename A<T>::type;
};

int main()
{
    A<double>::type a = 5.0;
    A<A<double>>::type d = 3.0;
    A<A<A<double>>>::type c = 9.5;
    return 0;
}

ตัวอย่างสด

person Marco A.    schedule 15.05.2018
comment
ไอ้บ้านี่มันฉลาดชะมัด ขอบคุณ! - person Thomas B.; 15.05.2018
comment
เดี๋ยวก่อน ดูเหมือนว่าจะใช้งานไม่ได้หากฉันสแต็กเพียง 2 A: A<A<double>>::type? - person Thomas B.; 15.05.2018
comment
ใช่ สิ่งนี้ใช้ไม่ได้กับกรณีการใช้งาน A‹A‹double››::type d = 3.0; - person PapaDiHatti; 15.05.2018
comment
ความเชี่ยวชาญเฉพาะด้านที่สามมีไว้เพื่ออะไร? ดูเหมือนว่าจะทำงานโดยไม่ต้อง - person Thomas B.; 15.05.2018
comment
@โทมัสบี ใช่ อันที่สามคือโค้ดทดสอบที่เหลือ ลบออก. - person Marco A.; 15.05.2018
comment
ฉันคิดว่านี่เป็นตัวอย่างที่ดีของแนวทางที่มีใจเป็นขั้นตอนเทียบกับแนวทางที่มีใจที่ใช้งานได้ การใช้ conditional_t (คล้ายกับ if และ else) เป็นแนวทางที่คำนึงถึงขั้นตอน ในขณะที่ความเชี่ยวชาญพิเศษของเทมเพลตเป็นแนวทางที่คำนึงถึงการใช้งาน (คล้ายกับการจับคู่รูปแบบ) มันอาจจะคุ้มค่าที่จะกล่าวถึงว่าการสืบทอดจาก A<T> อาจจะคุ้มค่าหากมีคลาสมากกว่าแค่นามแฝงประเภท - person Pharap; 15.05.2018

นอกจากการพิมพ์ผิดของคุณว่าขาด typename แล้ว ปัญหาที่นี่:

using type = std::conditional_t<
    is_a<T>::value,
    T::type, // if it's an A, go deeper
    T>;      // if not, we're done

คือ std::conditional ไม่ใช่การลัดวงจร เมื่อ T ไม่มีสมาชิก type จะทำให้เกิดข้อผิดพลาด

คุณสามารถเขียนฟังก์ชันเมตาเพื่อแยกประเภทภายในแบบวนซ้ำได้:

template<class T>
struct extract_type {
    using type = T;
};

template<class T> class A;

template<class T>
struct extract_type<A<T>> {
    using type = typename extract_type<T>::type;
};

template<typename T>
struct A
{
    using type = typename extract_type<T>::type;
};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}
person llllllllll    schedule 15.05.2018

ทางเลือกแทนคำตอบของ Marco (ถูกต้อง) คุณอาจต้องการใส่ตรรกะการเลือกประเภทนี้บางส่วนลงในคลาสลักษณะ:

// step 1 - predeclare the template A

template<typename T> struct A;

// define a default specialisation of a traits type
template<class T> struct ATraits
{
    using type = T;
};

// specialise the traits for the A<T> case
template<class T> struct ATraits<A<T>>
{
    using type = typename A<T>::type;
};

// now define the A template default specialisation
template<typename T>
struct A
{
    using type = typename ATraits<T>::type;
};

int main()
{
    A<A<A<A<A<double>>>>>::type d = 3.0;
    return 0;
}
person Richard Hodges    schedule 15.05.2018

คุณสามารถใช้ enable_if และ SFINAE เพื่อเลือกประเภทที่อยู่ด้านในสุดดังนี้:

template<typename T, class Enable = void>
struct A {
    using type = T;
};

template<typename T>
struct A<T, std::enable_if_t<!std::is_same_v<T, typename T::type>>> {
   using type = typename T::type;
};
person Jodocus    schedule 15.05.2018