Der Versuch, sich auf die Gültigkeit der Arraygröße zu spezialisieren:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return 0; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
operator int () const { return -p; }
};
int main() {
std::cout << absolute<5>() << std::endl;
std::cout << absolute<-5>() << std::endl;
std::cout << absolute<0>() << std::endl;
}
Fehler 1:
Der obige Code funktioniert gut mit gcc, kann jedoch nicht mit clang kompiliert werden .
Clang erzeugt den Fehler: Neudefinition der Vorlagenstruktur 'absolut'
Wer hat Recht?
Problem Nr. 2:
Sowohl mit gcc als auch mit clang (wenn wir die negative Spezialisierung entfernen, um clang wieder ins Spiel zu bringen), ist nicht klar, warum absolute<0>()
die Basisvorlage ausgewählt wird. Es ist nichts falsch mit int[0]
als auch mit std::void_t<int[0]>
denen scheint mehr spezialisiert zu werden:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return -1; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
int main() {
std::cout << absolute<5>() << std::endl; // 5
std::cout << absolute<0>() << std::endl; // -1, why not 0?
}
Und ... wenn die Basisvorlage nur ohne Implementierung deklariert wird, als:
// base template
template<int p, typename T = void>
struct absolute;
Sowohl gcc als auch clang konnten nicht kompiliert werden und beschwerten sich über die ungültige Verwendung eines unvollständigen Typs für den Aufruf : absolute<0>()
. Auch wenn es zum Spezialfall zu passen scheint.
Warum ist das so?
quelle
template<int p> struct absolute<p, typename std::void_t<int[p]>>
undtemplate<int p> struct absolute
ist das nicht ein Duplikat?int[0]
ist durch den ISO C ++ Standard timsong-cpp.github.io/cppwp/n4659/dcl.array#1 verboten "sein Wert muss größer als Null sein"Antworten:
Informationen zu Clangs Neudefinitionsfehler finden Sie in dieser Frage .
Ursprünglich wurden Vorlagen-IDs von Alias-Vorlagen wie z. B.
std::void_t
einfach durch ihren Aliasing-Typ ersetzt, ohne die Argumente auf einen Ersetzungsfehler zu überprüfen. Dies wurde mit der CWG-Ausgabe 1558 geändert . Dies änderte nur den Standard, sodass ein Ersetzungsfehler in den durchzuführenden Vorlagenargumenten erforderlich war, klärte jedoch nicht, ob zwei Vorlagen, die nach dem Ersetzen des Alias gleichwertig wären, als gleichwertig angesehen werden sollten. Clang betrachtet sie als gleichwertig, GCC jedoch nicht. Dies ist die offene CWG-Ausgabe 1980 .Mit
-pedantic-errors
GCC meldet sich schon ein harter Fehler fürin der Spezialisierung
weil angeblich die Arraygröße kein konstanter Ausdruck ist. Die Größe eines Arrays muss ein konvertierter konstanter Ausdruck vom Typ sein
std::size_t
. Ein konvertierter konstanter Ausdruck darf nur nicht einschränkende Konvertierungen verwenden. Es ist also wahr, dass-p
mitp = 5
konvertiert instd::size_t
kein konstanter Ausdruck ist, was den Typint[-p]
schlecht formt, aber ich denke, dass dies einen Substitutionsfehler verursachen sollte, keinen harten Fehler. [temp.deduct / 8] des C ++ 17-Standards (Entwurf N4659) lautet:Und das gilt hier. Die nicht normativen Beispiele im Anschluss an das Zitat enthalten sogar negative Arraygrößen als Beispiel für einen Substitutionsfehler.
Es ist besonders seltsam, dass für
absolute<-5>()
GCC der entsprechende Fehler auf dem nicht gemeldet wirdSpezialisierung, wo
int[p]
ausgewertet werden würde,int[-5]
welche auch keine konvertierte konstante Ausdrucksgröße hat.absolute<0>()
wählt die primäre Vorlage, da die Arraygrößen größer als Null sein müssen, wodurch die Teilspezialisierungen beide nicht realisierbar sind. Arrays mit der Größe Null sind eine Spracherweiterung, mit der-pedantic-errors
in GCC und Clang deaktiviert werden kann .quelle
void_t
Werken für clang und gcc: godbolt.org/z/-2C8mJ, damit es mit der CWG-Ausgabe 1558 zu tun zu haben scheint ... oder behandelt es auch die CWG-Ausgabe 1980?make_void<Ts...>::type
, was ein abhängiger Typ ist.