Beim Kompilieren des folgenden Beispiels ist das folgende Problem aufgetreten:
template <int N>
class Matrix {
public:
template <int Idx>
int head() {
return Idx;
}
};
template <typename T>
class Test {
static constexpr int RayDim = 3;
public:
int func() const {
Matrix<RayDim> yF;
return yF.head<1>();
// ^ is template keyword required here?
}
};
struct Empty {};
void test() {
Test<Empty> t;
}
Link zum Compiler Explorer: https://godbolt.org/z/js4XaP
Der Code wird mit GCC 9.2 und MSVC 19.22 kompiliert, jedoch nicht mit clang 9.0.0. Clang gibt an, dass ein Vorlagenschlüsselwort erforderlich ist. Wenn static constexpr int RayDim = 3;
in int func() const
Clang bewegt wird, akzeptiert es.
Ist das Template-Schlüsselwort, wie im Codeblock als Kommentar angegeben, erforderlich für yF.head<1>()
?
Matrix<RayDim>
ist kein abhängiger Typ und daher ist das Schlüsselwort nicht erforderlich. Ich kann später Zeit für eine Antwort haben.Antworten:
Das
template
Schlüsselwort sollte hier nicht erforderlich sein, daher ist clang falsch, um das Programm abzulehnen.Alle C ++ Standard-Abschnitts- und Absatznummern und Anführungszeichen unten sind für den C ++ 17-Entwurf N4659 und den aktuell verknüpften C ++ 20-Entwurf identisch.
Die Anforderung für
template
nach einem.
oder->
oder::
Token beim Benennen einer Mitgliedsvorlage befindet sich in [temp.names] / 4 . Der Absatz listet zuerst Fälle auf, in denen das Schlüsselwort nicht zulässig ist, dann Fälle, in denen es optional ist und keinen Unterschied macht. Dann:Ein "Mitglied einer unbekannten Spezialisierung" ist ein Mitglied eines abhängigen Typs, der nicht "die aktuelle Instanziierung" ist. Die Frage ist also, ob
Matrix<RayDim>
es sich um einen abhängigen Typ handelt. Dazu schauen wir uns [temp.dep.type] / 9 an:Matrix<RayDim>
ist eindeutig kein Vorlagenparameter, keine Art von Mitglied, cv-qualifiziert, ein Array-Typ, ein Funktionstyp oder angegeben durchdecltype
. Es ist ein zusammengesetzter Typ, verwendet jedoch nur einen Vorlagennamen und einen Ausdruck und wird daher nicht aus einem anderen Typ erstellt.Damit bleibt der Fall der einfachen Vorlagen-ID . Der Vorlagenname
Matrix
ist kein Vorlagenparameter. Das VorlagenargumentRayDim
ist ein Ausdruck. Überprüfen Sie nun, ob es typ- oder wertabhängig ist." Typabhängig " ist in [temp.dep.expr] definiert . Nur Absatz 3 kann für eine einzelne Kennung gelten wie
RayDim
:RayDim
sicherlich enthält keine__func__
, Template-id , Umwandlung-function-id , verschachtelte-name-Spezifizierer oder qualifizierte Nummer . Die Namenssuche findet die statische Elementdeklaration der Klassenvorlage. Diese Deklaration vonRayDim
ist sicherlich kein Template-Parameter , keine Member-Funktion oder eine strukturierte Bindungsdeklaration, und ihr Typconst int
ist sicherlich kein abhängiger Typ oder Array-Typ und enthält keinen Platzhaltertyp. IstRayDim
also nicht typabhängig." Wertabhängig " ist in [temp.dep.constexpr] definiert . Die einzigen Fälle, die für eine einzelne Kennung wie
RayDim
gelten können, sind in Absatz 2 aufgeführt:Von oben
RayDim
ist nicht typabhängig. Es ist sicherlich kein Vorlagenparameter oder eine Elementfunktion. Es ist ein statisches Datenelement und abhängiges Mitglied der aktuellen Instanziierung, aber es wird in dem initialisierten Mitglied-declarator . Das heißt, das "= 3
" wird in der Klassendefinition angezeigt, nicht in einer separaten Elementdefinition. Es ist eine Konstante mit Literal-Typ, aber ihr Initialisierer3
ist nicht wertabhängig.Ist
RayDim
also nicht wert- oder typabhängig. DaherMatrix<RayDim>
ist es kein abhängiger Typ,yF.head
kein Mitglied einer unbekannten Instanziierung, und dastemplate
vorhergehende Schlüsselworthead
ist optional und nicht erforderlich. (Dies ist zulässig, da es sich nicht um einen "Nur-Typ-Kontext" handelt undhead
tatsächlich eine Mitgliedsvorlage benennt.)quelle
Haftungsausschluss
Dies ist keine Antwort, sondern ein langer Kommentar
Meine Kenntnisse als Sprachanwalt sind zu gering, um den Standard vollständig zu verstehen, aber hier sind einige Dinge, die ich beim Experimentieren mit dem Code entdeckt habe. Alles, was folgt, basiert auf meinem (alles andere als perfekten) Verständnis der Angelegenheit und benötigt wahrscheinlich einige Überprüfungen.
Ermittlung
Zunächst ging ich zu einer vollständig definierten Standardversion (C ++ 17), damit wir an einer genau definierten Implementierung arbeiten.
Wenn man sich diesen Code ansieht, scheint es, dass MSVC immer noch einige Probleme hat (mit seiner Suche, denke ich?), Wenn es um die Instanziierung und Neudefinition von Vorlagen geht. Ich würde MSVC in unserem Szenario nicht so sehr vertrauen.
Lassen Sie uns darüber nachdenken, warum wir das
template
Schlüsselwort bei brauchen könntenAbhängige Namen
In Vorlagen müssen wir dem Compiler manchmal bei der Entscheidung helfen, ob sich ein Name auf ihn bezieht
int T::x = 0
,struct T::x {};
odertemplate <typename U> T::foo<U>();
Wenn wir uns auf einen Wert beziehen, tun wir nichts. Wenn wir uns auf einen Typ beziehen, müssen wir verwenden
typename
. Und wenn wir uns auf eine Vorlage beziehen, die wir verwendentemplate
. Mehr zu diesem Thema finden Sie hier .Ich verstehe die Standardspezifikation nicht, wenn es um die tatsächliche Definition eines abhängigen Namens geht, aber hier sind einige Beobachtungen.
Beobachtungen
Schauen wir uns den Referenzcode an
Normalerweise
RayDim
sollte es sich um einen abhängigen Namen handeln (da er sich in der Vorlage befindetTest
), der auchMatrix<RayDim>
zu einem abhängigen Namen führen würde. Nehmen wir zunächst an, dass es sichMatrix<RayDim>
tatsächlich um einen abhängigen Namen handelt. Dies macht auchMatrix<RayDim>::head
einen abhängigen Namen. DaMatrix<RayDim>::head
es sich um eine Vorlagenfunktion handelt, handelt es sich um eine Vorlage an sich, und es gelten die Regeln für abhängige Namen von oben, sodass wir dastemplate
Schlüsselwort verwenden müssen. Darüber beschwert sich Clang.Da es jedoch innerhalb derselben Vorlage
RayDim
definiert istTest
undfunc
auch innerhalb derselben Vorlage definiert ist und keine Vorlagenfunktion an sich, denke ich nicht, dass diesRayDim
tatsächlich ein abhängiger Name im Kontext von istfunc
. Darüber hinausRayDim
stützt sich nicht auf die Vorlagenargumente vonTest
. In diesem FallMatrix<RayDim>
undMatrix<RayDim>::head
jeweils würden, werden nicht-abhängige Namen, die uns die weglassen könnentemplate
Schlüsselwort. Aus diesem Grund kompilieren gcc (und msvc).Wenn wir auch eine Vorlage
RayDim
erstellen würden, wie hiergcc würde es auch als abhängigen Namen behandeln (was richtig ist, da es später möglicherweise eine Vorlagenspezialisierung gibt, sodass wir es zu diesem Zeitpunkt noch nicht wissen). In der Zwischenzeit akzeptiert msvc gerne alles, was wir darauf werfen.
Fazit
Es scheint, als würde es darauf ankommen, ob
RayDim
es sich um einen abhängigen Namen im Kontext von handeltTest<T>::func
oder nicht. Clang glaubt es ist, gcc nicht. Von einigen weiteren Tests sieht es aus wie MSVC-Seiten mit Klirren auf diesem. Aber es macht auch irgendwie sein eigenes Ding, also wer weiß?Ich würde mich hier auf die Seite von gcc stellen, da ich keine Möglichkeit sehe
RayDim
, an dem Punkt, an demfunc
instanziiert wird , abhängig zu werden .quelle