2016-11-07 1 views
7

Diese Frage ist ein Follow-up von Moving a member function from base class to derived class breaks the program for no obvious reason (dies ist ein gutes Beispiel dafür, warum soll man using namespace std; nicht verwenden)Matrizenabhängiger Basiselement behoben ist nicht richtig

, wo die Antworten von this-> eineine abhängigen Vorlagennamen qualifizieren vorschlagen (was in der Tat der Weg ist, wenn man sich auf solche abhängigen Mitglieder bezieht). Es scheint jedoch ein Problem zu geben, daher liste ich ein minimales Beispiel auf, das das Problem reproduziert.

Betrachten Sie den Code:

#include <iostream> 
#include <bitset> 

using namespace std; 

template<class T> 
struct B 
{ 
    T bitset{}; 
}; 

template<class T> 
struct D : B<T> 
{ 
    bool foo() 
    { 
     return this->bitset < 32; 
    } 
}; 

int main(){} 

Live on Coliru

Die verwirrende Sache ist, dass, obwohl this->bitset das Mitglied noch verwirrt B<T>::bitset, der Compiler beziehen soll, ist und glaubt, dass wir auf std::bitset<std::size_t> zu beziehen versuchen. Der Fehler tritt sowohl bei gcc6 als auch bei clang3.7 auf. Irgendwelche Ideen, warum das passiert? Qualifizieren Sie es mit B<T>::bitset funktioniert aber.

Error (wörtlich):

In member function 'bool D<T>::foo(T, std::__cxx11::string)': cpp/scratch/minimal.cpp:24:22: error: invalid use of 'class std::bitset<1ul>'

EDIT

Das sieht für mich wie ein Parsing/Name-Lookup-Fehler. Wenn wir < durch irgendeinen anderen Vergleichsoperator ersetzen (Danke @Leon für die Bemerkung), z.B.

return this->bitset == 32; 

das Programm kompiliert. Also denke ich in this->bitset < 32 der Parser glaubt, dass wir versuchen, eine Vorlage zu instanziieren (< Zeichen), und wir haben vergessen, die > zu schließen. Habe aber auch keine Ahnung, ob das tatsächlich ein Bug ist oder wie die Sprache funktionieren soll.

+2

Ich denke, der Compiler sagt Ihnen nur subtil, dass Sie wirklich std :: bitset;) anstelle einer repurposed Ganzzahl verwenden sollten. – rubenvb

+1

Wahrscheinlich wegen der Art und Weise, wie 'name-lookup' funktioniert? 'name-lookup' in einem einfachen Fall überprüft den abgeleiteten Namespace und dann den Basis-Namespace und dann den globalen Bereich.Da "Base" hier abhängig ist, kann es nicht in seinen Gültigkeitsbereich schauen und es herausfinden, bis es eine Instanziierung findet. – Arunmu

+0

@Arunmu: Ich erwarte, dass die Namenssuche bei Datenmembern und Elementfunktionen zurückgestellt wird. das scheint anders zu sein. –

Antwort

7

tl; dr Es sieht so aus, als ob dies eine bewusste Entscheidung ist, speziell um die bereits verwendete alternative Syntax zu unterstützen.

Ein ungefährer Durchlauf des standardese unter:

this-> B < 
     ^
  • könnte dies entweder der Beginn einer Vorlage ID oder ein weniger als sein, so lassen Sie sich beide überprüfen!
    1. this->B tut Name etwas, aber es ist eine Schablone B<T>, so halten
    2. B auf seinen eigenen Namen auch etwas los, eine Klassenvorlage B<T>
    3. warten, sind sie die gleiche Sache!Das heißt, wir this->B<T> als Qualifier verwenden, und es ist nicht ein weniger-als schließlich

Im anderen Fall,

this->bitset 

Erlös identisch bis der dritte Schritt, wenn es realisiert, gibt es zwei verschiedene Dinge namens bitset (eine Vorlage Klassenmitglied und eine Klassenvorlage), und gibt einfach auf.


Dies geht aus einem Arbeitsentwurf um Ich habe liegen, also nicht unbedingt die letzte, aber:

3.4.5 Klasse Mitglied Zugang [basic.lookup.classref]

1 In einer Klasse Mitglied Zugriff Ausdruck (5.2.5), wenn die. oder -> Token werden sofort durch einen Bezeichner gefolgt, gefolgt von einem < muss der Bezeichner seine nachgeschlagen, um zu bestimmen, ob die < ist der Anfang einer Schablone Argumentliste (14.2) oder ein weniger-als-Operator. Der Bezeichner wird zuerst in der Klasse des Objektausdrucks nachgeschlagen. Wenn die ID nicht gefunden wird, wird sie im Kontext des gesamten postfix-Ausdrucks nachgeschlagen und muss eine Klassenvorlage angeben. Wenn die Suche in die Klasse des Objekts Ausdruck einer Vorlage findet, der Name ist auch sah im Kontext des gesamten Postfix-Ausdruck und

  • , wenn der Name nicht gefunden wird, wird der Name gefunden in der Klasse des Objekts wird der Ausdruck verwendet, ansonsten
  • Wenn der Name im Kontext des gesamten postfix-Ausdrucks gefunden wird und keine Klassenvorlage heißt, wird der in der Klasse des Objektausdrucks gefundene Name verwendet, andernfalls
  • Wenn der gefundene Name eine Klassenvorlage ist, muss sie sich auf die Sam Die Entität ist diejenige, die in der Klasse des Objektausdrucks gefunden wird, andernfalls ist das Programm schlecht gebildet.

Also, in jedem Ausdruck wie this->id < ..., hat es Fälle zu behandeln, wo id<... der Beginn einer Vorlage Kennung (wie this->B<T>::bitset).

Es prüft immer noch das Objekt zuerst, aber wenn this->id eine Vorlage findet, gelten weitere Schritte. Und in Ihrem Fall wird this->bitset vermutlich als Vorlage betrachtet, da es immer noch von T abhängt, also findet es den Konflikt std::bitset und scheitert an der dritten Kugel oben.

+3

Danke, ich muss sagen, dass dies einer der dunkelsten Fehler war, die ich jemals in einem C++ Code gesehen habe. – vsoftco

+0

Einverstanden, es ist irgendwie überraschend. Vermutlich wird es irgendwann unmöglich zu entscheiden, was ein Ausdruck ohne irgendeine Art von Regel bedeutet. – Useless

+0

Ich schätze, dass man zu Beginn mit einer mehrdeutigen Grammatik anfangen muss:/ –

Verwandte Themen