dentang / gcc inkonsistensi dalam spesialisasi kelas

9

Saya menemukan masalah ini ketika mencoba untuk mengkhususkan tuple_size/ tuple_elementuntuk 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;
}

https://godbolt.org/z/ztuRSq

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?

ofo
sumber
3
Ini dapat disederhanakan bahkan lebih .
Evg
3
ICC dan MSVC keduanya gagal dikompilasi juga.
ChrisMM
@ Evg Mengejutkan bahwa gccmengkompilasi itu, mengingat tidak mengkompilasi ini ...
Max Langhof
1
FWIW ini harus menjadi buruk jika saya tidak sepenuhnya salah (untuk alasan yang sama bahwa ini adalah buruk).
Max Langhof
1
karena kami mengutip standar, saya menambahkan tag pengacara bahasa.
Guillaume Racicot

Jawaban:

3

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.

n314159
sumber
Apakah sfinae_v_t<T, std::is_integral_v<T>>dan sfinae_v_t<T, !std::is_integral_v<T>>diperlakukan sebagai jenis yang sama? Secara semantik, mereka tidak.
Ofo
@GuillaumeRacicot Sangat mungkin, tapi saya ingin mengerti persisnya. Sebagai contoh, standar juga mengatakan "Nama dependen tidak dapat diperiksa ketika menyatakan spesialisasi parsial, tetapi akan diperiksa ketika mengganti ke spesialisasi parsial." Tidakkah itu berarti apakah mereka jenis yang sama harus diputuskan setelah mengganti T dalam spesialisasi sebagian karena sfinae_v_t<T>bergantung pada T? Dalam hal ini, mereka tidak akan sama karena salah satu dari mereka akan terbentuk dengan buruk.
Ofo
@ saya harus mengatakan, saya tidak yakin. Agak mindfuck untuk berpikir tentang keduanya karena salah satunya tidak akan menjadi tipe dan menggunakan keduanya dalam konteks non-template akan menghasilkan kesalahan kompilasi karena 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 (yaitu intsudah diganti) dan kemudian ada jenis yang benar-benar di salah satu dari mereka, jadi kita tidak perlu membandingkan mereka secara abstrak.
n314159
1
Menggali lebih dalam, saya menemukan ini dari sini . SFINAE harus bekerja dengan baik dengan alias template, jika 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.
Ofo