Ich kam dieses Problem auf bei dem Versuch , sich zu spezialisieren tuple_size
/ tuple_element
eine benutzerdefinierte Klasse in C ++ 17 für strukturierte Bindung.
Der folgende Code wird in GCC kompiliert, jedoch nicht in Clang (beide Trunk-Versionen, siehe Link unten).
#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;
}
Dies ist der Fehler von 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
Ist dies ein Fehler in einem der Compiler oder ruft der obige Code UB auf?
gcc
compiliert , die, wie es zu sehen , nicht kompiliert diese ...Antworten:
Was ich unten (unter OLD POST ) erzähle, sollte bis zu einem gewissen Grad zutreffen, aber das eigentliche Problem dabei ist, dass SFINAE falsch verwendet wird, daher bin ich mir nicht mehr so sicher, dass dies ein Fehler in gcc ist.
Eine Alias-Deklaration muss immer erfolgreich sein, Sie können dort keine SFINAE ausführen, da es sich nicht um eine Klassen- oder Funktionsdeklaration oder Spezialisierungen handelt (dies ist sinnvoll, da Sie keine Aliase spezialisieren können). Wenn die Aliasdeklaration nicht erfolgreich ist, ist das Programm fehlerhaft. Daher kann der Compiler davon ausgehen, dass die Alias-Deklaration erst dann erfolgreich sein wird, wenn Sie sie zwingen, eine solche Vorlage zu instanziieren.
Daher ist es für den Compiler durchaus akzeptabel zu glauben, dass dies
sfinae_v_t<T,...>
immerT
dann der Fall ist, wenn das Programm nicht schlecht geformt ist. Daher wird sich herausstellen, dass in allen Fällen, in denen das Programm nicht schlecht geformt ist, die Teilspezialisierung nicht spezialisiert ist, und als solche wird es Ihnen sagen, dass dies schlecht geformt ist. (Das macht Clang).Ich glaube nicht, dass der Compiler dazu gezwungen ist. Und wenn dies nicht der Fall ist und nur denkt, "Ok,
sfinae_v_t
ist irgendein Typ, was auch immer.", Dann ist es nicht offensichtlich, dass dies eine erneute Erklärung ist. Ich denke, bis wir einen von ihnen instanziieren, ist es nichts Falsches, keinen Fehler zu werfen.Aber wenn wir es instanziieren, sollte es entweder das Problem geben, dass wir eine erneute Deklaration haben oder dass das Programm aufgrund
std::enable_if
des Template-Arguments schlecht geformt ist . GCC sollte mindestens einen von ihnen abholen, tut dies aber nicht.Dies gilt auch absolut nicht für das einfachere Beispiel ohne
std::enable_if
. Ich denke immer noch, dass dies ein Fehler in GCC ist, aber ich bin so verrückt, dass ich das nicht mehr mit Sicherheit sagen kann. Ich würde nur sagen, jemand sollte das als Fehler melden und die Leute von gcc darüber nachdenken lassen.ALTER POST
Dies ist ein Fehler in gcc. Der Standard gibt uns Regeln für die Konvertierung einer Klassenvorlage in Funktionsvorlagen. Eine Klassenvorlage ist spezialisierter als eine andere, wenn ihre Funktion in der Reihenfolge der Teilfunktionsvorlagen vor der anderen steht.
Ich habe die Funktionen hier und jetzt erstellt. Gcc behauptet, dass das Aufrufen nicht eindeutig ist. Daher muss auch gesagt werden, dass die Klassenvorlagen gleichermaßen angegeben sind.
Hinweis: Wenn Sie den Standard sorgfältig lesen, stimmt der Compiler in meinem Kopf dem Klirren zu.
quelle
sfinae_v_t<T, std::is_integral_v<T>>
undsfinae_v_t<T, !std::is_integral_v<T>>
werden als gleiche Typen behandelt? Semantisch sind sie nicht.sfinae_v_t<T>
abhängtT
? In diesem Fall wären sie nicht gleich, da beide schlecht geformt sind.enable_if_t
. Meine beste Lektüre des Standards ist, dass es keine Rolle spielt, ob sie gleich sind oder nicht. Für die Teilreihenfolge vergleichen wir immer die Templare-Parameterform einer Funktion mit der Template-Argumentform der anderen (dh dieint
ist bereits ersetzt), und dann gibt es in einer von ihnen einen echten Typ, sodass wir nicht vergleichen müssen sie abstrakt.template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;
würde es auch nicht funktionieren. Ich werde den Fehler gegen gcc einreichen, bin mir aber nicht sicher, ob gcc dort falsch ist. Vielen Dank.