Saya menemukan masalah ini ketika mencoba untuk mengkhususkan tuple_size
/ tuple_element
untuk kelas khusus di C ++ 17 untuk penjilidan terstruktur.
Kode di bawah ini dikompilasi dalam GCC, tetapi tidak dalam dentang (kedua versi trunk, lihat tautan di bawah).
#include <type_traits>
template<typename T, typename... Ts>
using sfinae_t = T;
template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;
template <typename T>
struct Test;
template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};
void f() {
Test<int> t;
}
Ini adalah kesalahan yang diberikan oleh dentang:
<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Compiler returned: 1
Apakah ini bug di kompiler atau apakah kode di atas memanggil beberapa UB?
gcc
mengkompilasi itu, mengingat tidak mengkompilasi ini ...Jawaban:
Apa yang saya katakan di bawah (di bawah POST LAMA ) harus benar untuk gelar, tetapi masalah sebenarnya dengan ini, bahwa SFINAE digunakan secara salah, maka saya tidak yakin lagi bahwa ini adalah bug di gcc.
Deklarasi alias harus selalu berhasil, Anda tidak dapat SFINAE di sana, karena itu bukan deklarasi kelas atau fungsi atau spesialisasi (yang masuk akal, karena Anda tidak dapat mengkhususkan alias). Jika deklarasi alias tidak berhasil, programnya salah bentuk. Oleh karena itu kompilator dapat berasumsi bahwa itu tidak akan pernah sampai pada kasus bahwa deklarasi alias tidak berhasil sampai Anda memaksanya untuk membuat contoh seperti template.
Oleh karena itu sangat dapat diterima bagi kompiler untuk berpikir bahwa
sfinae_v_t<T,...>
itu selaluT
, karena itu akan terjadi, ketika program tidak terbentuk dengan buruk. Oleh karena itu ia akan melihat, bahwa dalam semua kasus di mana program tidak terbentuk dengan buruk, spesialisasi parsial tidak mengkhususkan dan dengan demikian ia akan memberi tahu Anda bahwa ini adalah dibentuk buruk. (Itulah yang dilakukan dentang).Saya tidak berpikir bahwa kompiler terpaksa melakukan ini. Dan jika tidak, dan hanya berpikir "Oke,
sfinae_v_t
ada beberapa jenis, apa pun.", Maka tidak jelas bahwa ini adalah deklarasi ulang. Jadi saya pikir sampai kita instantiate salah satunya tidak ada yang salah dengan tidak melempar kesalahan.Tetapi ketika kita instantiate itu harus ada masalah bahwa kita memiliki deklarasi ulang atau bahwa program tersebut tidak terbentuk karena
std::enable_if
, tergantung pada argumen template. GCC harus mengambil setidaknya satu dari mereka tetapi tidak melakukannya.Ini juga sama sekali tidak berlaku untuk contoh yang lebih mudah tanpa
std::enable_if
. Jadi saya masih berpikir ini adalah bug di GCC, tapi saya cukup berpikiran bahwa saya tidak bisa mengatakan itu dengan pasti lagi. Saya hanya akan mengatakan, seseorang harus melaporkan itu sebagai bug dan membiarkan orang-orang dari gcc memikirkannya.POST LAMA
Ini adalah bug di gcc. Standar memberi kita aturan untuk mengonversi templat kelas di templat fungsi. Satu templat kelas lebih khusus daripada yang lain jika fungsinya datang sebelum yang lain dalam urutan templat fungsi parsial.
Saya membuat fungsi di sini dan sekarang klaim gcc bahwa memanggil mereka ambigu, maka itu juga harus mengatakan bahwa templat kelas sama-sama ditentukan.
Catatan: Membaca standar dengan seksama, kompiler di kepala saya setuju dengan dentang.
sumber
sfinae_v_t<T, std::is_integral_v<T>>
dansfinae_v_t<T, !std::is_integral_v<T>>
diperlakukan sebagai jenis yang sama? Secara semantik, mereka tidak.sfinae_v_t<T>
bergantung padaT
? Dalam hal ini, mereka tidak akan sama karena salah satu dari mereka akan terbentuk dengan buruk.enable_if_t
. Bacaan terbaik saya dari standar adalah, bahwa tidak masalah apakah mereka sama atau tidak. Untuk pemesanan parsial kita akan selalu membandingkan bentuk parameter templare dari satu fungsi ke bentuk argumen templat yang lain (yaituint
sudah diganti) dan kemudian ada jenis yang benar-benar di salah satu dari mereka, jadi kita tidak perlu membandingkan mereka secara abstrak.template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;
tidak , tidak akan bekerja dengan baik. Saya akan melanjutkan dan mengajukan bug terhadap gcc, tetapi tidak begitu yakin apakah gcc salah di sana. Terima kasih.