Bagaimana cara memeriksa jenis parameter template?

97

Misalkan saya memiliki fungsi template dan dua kelas

class animal {
}
class person {
}

template<class T>
void foo() {
  if (T is animal) {
    kill();
  }
}

Bagaimana cara melakukan pemeriksaan T adalah hewan? Saya tidak ingin memiliki sesuatu yang diperiksa selama waktu pengoperasian. Terima kasih

WhatABeautifulWorld
sumber
62
Saya akan menempatkan "hewan peliharaan" daripada "membunuh" :-)
JimBamFeng

Jawaban:

135

Penggunaan is_same:

#include <type_traits>

template <typename T>
void foo()
{
    if (std::is_same<T, animal>::value) { /* ... */ }  // optimizable...
}

Biasanya, itu adalah desain yang sama sekali tidak bisa diterapkan, dan Anda benar-benar ingin mengkhususkan :

template <typename T> void foo() { /* generic implementation  */ }

template <> void foo<animal>()   { /* specific for T = animal */ }

Perhatikan juga bahwa tidak biasa memiliki template fungsi dengan argumen eksplisit (tanpa deduksi). Ini tidak pernah terdengar, tetapi seringkali ada pendekatan yang lebih baik.

Kerrek SB
sumber
2
TThanks! Sebenarnya mereka berbagi BANYAK kode jadi saya tidak bisa menduplikasinya
WhatABeautifulWorld
3
@WhatABeautifulWorld: Anda selalu dapat memfaktorkan kode Anda sehingga bagian yang bergantung pada tipe dapat diturunkan ke fungsi khusus ...
Kerrek SB
1
Satu tindak lanjut cepat, jika saya menggunakan std :: is_same, maka TIDAK akan memperlambat kode untuk parameter template lainnya, bukan?
WhatABeautifulWorld
1
@WhatABeautifulWorld: Nilai sifat semuanya diketahui secara statis. Seharusnya tidak ada biaya waktu proses, asalkan kompiler Anda setengah layak. Periksa perakitan jika ragu.
Kerrek SB
2
@ AdriC.S .: Karena Ttidak disimpulkan, tidak banyak yang dapat Anda lakukan. Anda dapat membiarkan template utama tidak diterapkan dan membuat spesialisasi, atau Anda dapat menambahkan pernyataan statis dengan is_same.
Kerrek SB
37

Saya pikir hari ini, lebih baik digunakan, tetapi hanya dengan C ++ 17.

#include <type_traits>

template <typename T>
void foo() {
    if constexpr (std::is_same_v<T, animal>) {
        // use type specific operations... 
    } 
}

Jika Anda menggunakan beberapa jenis operasi khusus di if badan ekspresi tanpa constexpr, kode ini tidak akan dikompilasi.

Константин Гудков
sumber
8
alih-alih std::is_same<T, U>::valueAnda bisa menggunakan lebih pendek:std::is_same_v<T, U>
Fureeish
12

Di C ++ 17, kita bisa menggunakan varian .

Untuk menggunakan std::variant, Anda perlu menyertakan tajuk:

#include <variant>

Setelah itu, Anda dapat menambahkan std::variantkode Anda seperti ini:

using Type = std::variant<Animal, Person>;

template <class T>
void foo(Type type) {
    if (std::is_same_v<type, Animal>) {
        // Do stuff...
    } else {
        // Do stuff...
    }
}
Edwin Pratt
sumber
8
Bagaimana T dan Type terhubung?
mabraham
4
Jawaban ini bermasalah dalam beberapa hal. Selain itu kesalahan aktual ( typeyang merupakan nilai tipe Typeatau templat yang tidak masuk akal di sini) is_same_vtidak bermakna dalam konteks variant. "Sifat" yang sesuai adalah holds_alternative.
Pixelchemist
std::variantsama sekali tidak diperlukan di sini
tjysdsg
7

Anda dapat mengkhususkan template Anda berdasarkan apa yang diteruskan ke parameternya seperti ini:

template <> void foo<animal> {

}

Perhatikan bahwa ini membuat fungsi yang sepenuhnya baru berdasarkan jenis yang diteruskan sebagai T. Ini biasanya lebih disukai karena mengurangi kekacauan dan pada dasarnya merupakan alasan kami memiliki template di tempat pertama.

template boy
sumber
Hmm. Apakah metode ini benar-benar satu-satunya cara yang lebih disukai untuk mengkhususkan argumen template? Katakanlah saya memiliki 10 kelas anak berbeda yang perlu saya kelola di dalam fungsi template. Apakah saya benar-benar harus menulis 10 fungsi template yang berbeda untuk masing-masing kelas? Saya pikir saya mungkin kehilangan inti di sini.
Volkan Güven
Ini benar-benar ide yang bagus, jika seseorang tidak ingin menggunakan type_traits. Seperti seseorang yang disebutkan, logika utama dapat dilakukan dalam fungsi yang berbeda, yang menerima tanda tambahan untuk menunjukkan jenisnya, dan deklarasi khusus ini hanya dapat menyetel bendera yang sesuai dan langsung meneruskan semua argumen lain tanpa menyentuh apa pun. Jadi jika 10 kelas berbeda perlu ditangani, pada dasarnya itu adalah 10 baris untuk 10 definisi fungsi yang berbeda. Tetapi ini akan menjadi sangat rumit jika ada lebih dari 1 variabel template.
Harish Ganesan