incoerenza clang / gcc nella specializzazione di classe


9

Mi sono imbattuto in questo problema durante il tentativo di specializzarmi tuple_size/ tuple_elementper una classe personalizzata in C ++ 17 per l'associazione strutturata.

Il codice seguente viene compilato in GCC, ma non in clang (entrambe le versioni del trunk, vedere il link seguente).

#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

Questo è l'errore fornito da clang:

<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

Si tratta di un bug nel compilatore o il codice sopra invoca un po 'di UB?


3
Questo può essere semplificato ancora di più .
Evg

3
Anche ICC e MSVC non riescono a compilare.
ChrisMM,

@Evg è sorprendente che gcccompila che, visto che non viene compilato questo ...
Max Langhof

1
FWIW questo dovrebbe essere mal formato se non mi sbaglio completamente (per lo stesso motivo per cui questo è mal formato).
Max Langhof,

1
poiché stiamo citando lo standard, ho aggiunto il tag language-lawyer.
Guillaume Racicot,

Risposte:


3

Quello che dico sotto (sotto OLD POST ) dovrebbe essere vero fino a un certo punto, ma il vero problema con questo è che SFINAE è usato in modo errato, quindi non sono più sicuro che questo sia un bug in gcc.

Una dichiarazione alias deve sempre avere successo, non è possibile SFINAE lì, dal momento che non è una dichiarazione di classe o di funzione o specializzazioni (questo ha senso, dal momento che non è possibile specializzare gli alias). Se la dichiarazione di alias non riesce, il programma è mal formato. Quindi il compilatore può presumere che non si verificherà mai che la dichiarazione di alias non avrà successo fino a quando non la forzerai a creare un'istanza di tale modello.

Quindi è perfettamente accettabile per il compilatore pensare che ciò avvenga sfinae_v_t<T,...>sempre T, poiché ciò accadrà, quando il programma non è mal formato. Quindi vedrà che in tutti i casi in cui il programma non è mal formato, la specializzazione parziale non è specializzata e come tale ti dirà che questo è mal formato. (Questo è ciò che fa clang).

Non penso che il compilatore sia costretto a farlo. E se non lo fa, e pensa semplicemente "Ok, sfinae_v_tè un tipo, qualunque cosa", allora non è ovvio che si tratta di una dichiarazione. Quindi penso che fino a quando non avremo un'istanza di uno di essi non ci sia nulla di male nel non gettare un errore.

Ma quando creiamo un'istanza dovrebbe esserci il problema di avere una dichiarazione o che il programma è mal formato a causa std::enable_ifdell'argomento template. GCC dovrebbe prenderne almeno uno ma nessuno dei due.

Anche questo non si applica assolutamente all'esempio più semplice senza std::enable_if. Quindi penso ancora che si tratti di un bug in GCC, ma sono abbastanza stordito da non poterlo più dire con certezza. Direi solo che qualcuno dovrebbe segnalarlo come un bug e lasciare che le persone di gcc ci pensino.

POST VECCHIO

Questo è un bug in gcc. Lo standard ci fornisce le regole per convertire un modello di classe in modelli di funzione. Un modello di classe è più specializzato di un altro se la sua funzione precede l'altra nell'ordinamento parziale del modello di funzione.

Ho creato le funzioni qui e ora gcc afferma che chiamarle è ambigua, quindi dovrebbe anche dire che i modelli di classe sono ugualmente specificati.

Nota: leggendo attentamente lo standard, il compilatore nella mia testa è d'accordo con Clang.


Sono sfinae_v_t<T, std::is_integral_v<T>>e sfinae_v_t<T, !std::is_integral_v<T>>trattati come gli stessi tipi? Semanticamente, non lo sono.
ofo il

@GuillaumeRacicot Probabilmente, ma vorrei capire esattamente il perché. Ad esempio, lo standard dice anche "I nomi dipendenti non possono essere controllati quando si dichiara la specializzazione parziale, ma verranno controllati quando si sostituisce alla specializzazione parziale". Ciò non significa che debbano essere decisi lo stesso tipo dopo aver sostituito T in specializzazione parziale poiché sfinae_v_t<T>dipende da T? In tal caso, non sarebbero gli stessi perché uno dei due sarà mal formato.
ofo

Devo dire che non ne sono sicuro. È un po 'stupido anche solo pensare a quei due poiché uno di loro non sarà mai un tipo e usarli entrambi in un contesto non-template comporterà un errore di compilazione dovuto al enable_if_t. La mia migliore lettura dello standard è che non importa se sono uguali o meno. Per l'ordinamento parziale confronteremo sempre la forma del parametro templare di una funzione con la forma dell'argomento modello dell'altra (ovvero, intè già sostituita) e quindi c'è un tipo reale in una di esse, quindi non dobbiamo confrontare astrattamente.
n314159,

1
Scavando più a fondo, ho trovato questo da qui . SFINAE dovrebbe funzionare bene con gli alias dei template, altrimenti template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;non funzionerebbe neanche. Andrò avanti e presenterò il bug su gcc, ma non sono sicuro che gcc sia sbagliato lì. Grazie.
OFO
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.