Clang / GCC-Inkonsistenz in der Klassenspezialisierung

9

Ich kam dieses Problem auf bei dem Versuch , sich zu spezialisieren tuple_size/ tuple_elementeine 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;
}

https://godbolt.org/z/ztuRSq

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?

ofo
quelle
3
Dies kann noch weiter vereinfacht werden .
Evg
3
ICC und MSVC können ebenfalls nicht kompiliert werden.
ChrisMM
@Evg Es ist überraschend , dass gcccompiliert , die, wie es zu sehen , nicht kompiliert diese ...
Max Langhof
1
FWIW dies sollte schlecht geformt sein, wenn ich mich nicht völlig irre (aus dem gleichen Grund, aus dem dies falsch geformt ist).
Max Langhof
1
Da wir Standard zitieren, habe ich das Tag für Sprachanwälte hinzugefügt.
Guillaume Racicot

Antworten:

3

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,...>immer Tdann 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_tist 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_ifdes 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.

n314159
quelle
Sind sfinae_v_t<T, std::is_integral_v<T>>und sfinae_v_t<T, !std::is_integral_v<T>>werden als gleiche Typen behandelt? Semantisch sind sie nicht.
ofo
@ GuillaumeRacicot Möglicherweise, aber ich würde gerne verstehen, warum genau. Zum Beispiel heißt es im Standard auch : "Abhängige Namen können beim Deklarieren der Teilspezialisierung nicht überprüft werden, werden jedoch beim Ersetzen in die Teilspezialisierung überprüft." Bedeutet das nicht, dass nach dem Ersetzen von T in Teilspezialisierung zu entscheiden ist, ob sie vom gleichen Typ sind, da dies von sfinae_v_t<T>abhängt T? In diesem Fall wären sie nicht gleich, da beide schlecht geformt sind.
ofo
@ofo Ich muss sagen, ich bin nicht sicher. Es ist ein bisschen verrückt, überhaupt an diese beiden zu denken, da einer von ihnen niemals ein Typ sein wird und die Verwendung beider in einem Nicht-Vorlagen-Kontext aufgrund des zu einem Kompilierungsfehler führt 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 die intist bereits ersetzt), und dann gibt es in einer von ihnen einen echten Typ, sodass wir nicht vergleichen müssen sie abstrakt.
n314159
1
Ich grub tiefer und fand das von hier aus . SFINAE sollte mit Vorlagenaliasnamen gut funktionieren, sonst 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.
ofo