2013-05-09 8 views
5

EDIT: Dies ist kein Fehler, nur ich nicht über dependent name lookups in templated base classes wissen (die MSVC "hilft" ohne Fehler).Ist das ein Fehler in GCC?


Ich schrieb vor einiger Zeit eine Funktors Implementierung und ein einfacher „Ereignis“ Wrapper, der es verwendet. Es kompiliert in Ordnung unter MSVC, aber GCC gibt einen Fehler über eine Elementvariable in der Basisklasse subscribers, die nicht deklariert wird; Ändern subscribers zu this->subscribers löst das Problem (!). Es scheint nur mit dem seltsam wiederkehrenden Vorlagenmuster und mit partieller Vorlagenspezialisierung zu geschehen.

Simplified Quelle (sorry für die knifflige Vorlage Nutzung ...):

#include <vector> 

template<typename TEvent> 
struct EventBase 
{ 
protected: 
     std::vector<int> subscribers; 
}; 

template<typename TArg1 = void, typename TArg2 = void> 
struct Event : public EventBase<Event<TArg1, TArg2> > 
{ 
     void trigger(TArg1 arg1, TArg2 arg2) const 
     { 
       // Error on next line 
       auto it = subscribers.cbegin(); 
     } 
}; 

template<typename TArg1> 
struct Event<TArg1, void> : public EventBase<Event<TArg1> > 
{ 
     void trigger(TArg1 arg1) const 
     { 
       // Using `this` fixes error(?!) 
       auto it = this->subscribers.cbegin(); 
     } 
}; 

template<> 
struct Event<void, void> : public EventBase<Event<> > 
{ 
     void trigger() const 
     { 
       // No error here even without `this`, for some reason! 
       auto it = subscribers.cbegin(); 
     } 
}; 

int main() 
{ 
     return 0; 
} 

Bin ich irgendwo nicht definiertes Verhalten aufrufen? Ist meine Syntax irgendwie falsch? Ist das wirklich ein Fehler in GCC? Ist es vielleicht ein bekannter Fehler? Jeder Einblick würde geschätzt werden!

Weitere Details: Kompiliert mit g++ -std=c++11 main.cpp. Ich benutze GCC Version 4.7.2. Genaue Fehlermeldung:

main.cpp: In member function ‘void Event<TArg1, TArg2>::trigger(TArg1, TArg2) const’: 
main.cpp:17:15: error: ‘subscribers’ was not declared in this scope 
+2

Ich bin auch in das hineingeraten. Als Faustregel gilt: Wenn es sich um Vorlagen handelt, traue gcc immer über msvc (ich finde, dass gcc in 99,9% der Fälle richtig ist). –

+0

@JesseGood Es gibt sogar ein Duplikat [SO Frage] (http://Stackoverflow.com/q/11405/819272) von einem anderen MSVC Flüchtling. Anscheinend hat gcc es irgendwo in der Version 3.4 repariert und dabei alten Code zerstört. Aber hey, wer sagte, dass die Verkäufer die Fehlerkompatibilität als Verkaufsargument benötigen? – TemplateRex

Antwort

8

Dies ist ein Fehler in MSVC statt. Namen aus abhängigen Basisklassen müssen "thisambiguated" sein.

Der Grund ist, dass unqualifizierte Suche nach abhängigen Namen proceeds in two phases. In der ersten Phase ist die Basisklasse noch nicht bekannt und der Compiler kann den Namen nicht auflösen. MSVC implementiert keine zweiphasige Namenssuche und verzögert die Suche bis zur zweiten Phase.

Die vollständige Spezialisierung

template<> 
struct Event<void, void> : public EventBase<Event<> > 
{ 
     void trigger() const 
     { 
       // No error here even without `this`, for some reason! 
       auto it = subscribers.cbegin(); 
     } 
}; 

nicht unter diesem Problem leidet, weil sowohl die Klasse und ihre Basis reguläre Klassen sind, nicht Klassenvorlagen, und es gibt keine Vorlage Abhängigkeit zu beginnen.

Wenn die Portierung C++ Code von MSVC gcc/Clang, disambigusierung abhängig Namen-Suche und die template keyword disambiguation (dh Aufruf Memberfunktion Vorlage ::template, ->template oder .template Syntax) sind zwei der Feinheiten, die Sie mit zu tun haben (empty base optimization ist noch einer). Bei allen Normen-Compliance-Rhetorik wird dies aus Gründen der Rückwärtskompatibilität wahrscheinlich nie behoben sein.

+1

Könnten Sie das weiter erklären? Warum ist es im letzten Fall kein Fehler? (Nettes Wortspiel übrigens.) – Cameron

+1

@Cameron: Der letzte Fall ist keine Vorlage, es ist eine vollständige Spezialisierung. Alles darüber ist zur Kompilierungszeit bekannt. –

+3

@Cameron Die C++ FAQ hat eine [Erklärung] (http://www.parashift.com/c++faq-lite/nondependent-name-lookup-members.html), warum 'this->' in diesem Fall erforderlich ist . Es funktioniert auf MSVC, da es keine zweiphasige Namenssuche für Vorlagen implementiert. – Praetorian