Ambil jenis templat terdalam di dalam templat itu sendiri

apakah mungkin untuk mengambil tipe templat bertumpuk terdalam dari tipe yang sama dari dalam templat? Saya ingin mengambil tipe double dalam contoh berikut:

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;
}

Hal ini dilatarbelakangi oleh pertanyaan ini. Juga, saya menemukan postingan ini, menunjukkan bahwa ini mungkin ada hubungannya dengan penempatan kata kunci typename atau template, namun saya sendiri tidak dapat membuatnya berfungsi.


person Thomas B.    schedule 15.05.2018    source sumber


Jawaban (5)


Trik yang biasa dilakukan dengan pendekatan awal Anda adalah dengan menunda evaluasi:

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
Harus menerima yang ini karena lebih dekat dengan pertanyaan awal. Sekarang kami memiliki kedua pendekatan di sini, ini adalah hal yang baik - person Thomas B.; 15.05.2018

Kecuali saya melewatkan sesuatu, saya hanya akan mengkhususkan sebagian templat untuk mempermudah

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;
}

Sampel langsung

person Marco A.    schedule 15.05.2018
comment
Sialan, ini pintar. Terima kasih! - person Thomas B.; 15.05.2018
comment
Tunggu, sepertinya tidak berhasil kalau aku hanya menumpuk 2 A: A<A<double>>::type? - person Thomas B.; 15.05.2018
comment
Ya, ini tidak berfungsi untuk kasus penggunaan A‹A‹double››::type d = 3.0; - person PapaDiHatti; 15.05.2018
comment
Untuk apa spesialisasi ketiga? Tampaknya berfungsi tanpa - person Thomas B.; 15.05.2018
comment
@ThomasB. ya, yang ketiga adalah sisa kode tes. DIHAPUS. - person Marco A.; 15.05.2018
comment
Saya pikir ini adalah contoh yang baik dari pendekatan yang berpikiran prosedural vs pendekatan yang berpikiran fungsional. Penggunaan conditional_t (analog dengan if dan else) adalah pendekatan yang bersifat prosedural, sedangkan spesialisasi templat adalah pendekatan yang berpikiran fungsional (analog dengan pencocokan pola). Mungkin perlu disebutkan bahwa mewarisi dari A<T> mungkin bermanfaat jika kelasnya lebih dari sekadar alias tipe. - person Pharap; 15.05.2018

Selain kesalahan ketik karena kehilangan typename, masalahnya di sini:

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

apakah std::conditional bukan hubungan arus pendek. Jika T tidak memiliki anggota type, hal ini akan menyebabkan kesalahan.

Anda dapat menulis fungsi meta untuk mengekstrak tipe dalam secara rekursif:

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

Alternatif untuk jawaban Marco (benar). Anda mungkin ingin memasukkan beberapa logika pemilihan tipe ini ke dalam kelas sifat:

// 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

Anda dapat menggunakan enable_if dan SFINAE untuk memilih tipe terdalam seperti itu:

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