Spezialisierung basierend auf der Gültigkeit der Arraygröße

8

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?

Amir Kirsh
quelle
template<int p> struct absolute<p, typename std::void_t<int[p]>>und template<int p> struct absoluteist das nicht ein Duplikat?
Chipster
3
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"
LF
@LF also hier nicht scheitern: godbolt.org/z/sfSxam ist ein Fehler mit clang und gcc?
Amir Kirsh
@AmirKirsh Sie haben vergessen, Erweiterungen zu deaktivieren. godbolt.org/z/RB96uc
LF
Kompiliert auch in MSVC.
Eerorika

Antworten:

4

Informationen zu Clangs Neudefinitionsfehler finden Sie in dieser Frage .

Ursprünglich wurden Vorlagen-IDs von Alias-Vorlagen wie z. B. std::void_teinfach 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-errorsGCC meldet sich schon ein harter Fehler für

std::cout << absolute<5>() << std::endl;

in der Spezialisierung

template<int p>
struct absolute<p, typename std::void_t<int[-p]>>

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 -pmit p = 5konvertiert in std::size_tkein konstanter Ausdruck ist, was den Typ int[-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:

Wenn eine Ersetzung zu einem ungültigen Typ oder Ausdruck führt, schlägt der Typabzug fehl. Ein ungültiger Typ oder Ausdruck ist ein fehlerhafter Typ, bei dem eine Diagnose erforderlich wäre, wenn er mit den ersetzten Argumenten geschrieben würde.

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 wird

template<int p>
struct absolute<p, typename std::void_t<int[p]>>

Spezialisierung, 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-errorsin GCC und Clang deaktiviert werden kann .

Nussbaum
quelle
Implementierung unserer eigenen Version von void_tWerken 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?
Amir Kirsh
@AmirKirsh Mit Ihrer Definition umgehen Sie auch das CWG-Problem 1980. Beide beziehen sich darauf, wie Alias-Vorlagen, die in nicht abhängige Typen aufgelöst werden, behandelt werden. Ihre Definition wird in aufgelöst make_void<Ts...>::type, was ein abhängiger Typ ist.
Walnuss
@AmirKirsh Mir war nicht klar, dass GCC sich immer nur darüber beschwert, dass die Größe einer Spezialisierung kein konstanter Ausdruck ist. Ich habe die Antwort aktualisiert, um dies widerzuspiegeln, und es scheint mir noch deutlicher zu machen, dass GCC inkonsistent ist.
Walnuss