2015-10-20 5 views
6

Der folgende Code kompiliert nicht, es sei denn, die Kommentarzeile unkommentiert ist:C++ Operator-Komma/Vorwärts-Referenzcode kompiliert nicht

template <class T> struct R { static T& r(); }; 
struct M { 
    static char m(M&); 
    template <class T> static int m(const T&); 
}; 
template <class T> struct A; 
template <class T> 
struct B { 
    struct U { }; 
    struct V { M& operator,(U); }; 
    enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) }; 
}; 
template <class T> struct A { B<T> y; }; 
int main() 
{ 
// A<int>(); // Works if uncommented. 
    B<int>(); 
} 

Am Komma-Operator, denkt der Compiler, die es braucht A<int> zu abgeschlossen sein, auch wenn der Code nur in A<T>* übergibt. Ich verstehe nicht warum. Es schlägt sowohl mit clang als auch mit g ++ fehl. Clang sagt

h.cpp:13:36: error: field has incomplete type 'B<int>' 
template <class T> struct A { B<T> y; }; 
           ^
h.cpp:11:38: note: in instantiation of template class 'A<int>' requested here 
    enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) }; 
            ^
h.cpp:17:5: note: in instantiation of template class 'B<int>' requested here 
    B<int>(); 
    ^
h.cpp:8:8: note: definition of 'B<int>' is not complete until the closing '}' 
struct B { 
    ^
1 error generated. 

ich jetzt im Debugger sitze den Compiler Debuggen :-) Ich denke, was passiert ist, dass der Compiler Argument abhängige Lookup verwendet zu finden passende operator, s und die dazugehörigen Klassen und Namespaces für einen Zeiger auf eine Klasse enthalten die Klasse selbst, daher möchte der Compiler, dass die Klasse vollständig ist. Könnte sein.

+0

so, was Ihre Frage? –

+0

Warum kompiliert es nicht? Warum sollte der Compiler 'A ' vollständig sein, wenn er 'Operator betrachtet, (B :: V, A *)'? Warum funktioniert es, wenn die kommentierte Zeile unkommentiert ist? –

+3

Was hat der Fehler vom Compiler sagen? –

Antwort

0

erhalten Sie die gleiche Fehlermeldung, wenn Sie das Haupt zu ändern:

int main() 
{ 
    // A<int>(); // Works if uncommented. 

    auto& x = R<A<int>*>::r(); 
    B<int>(); 
} 

Ich bin auf einem Bein hier los zu gehen und sagen, dass es, wie Sie angedeutet. Das Erwähnen eines Zeigers auf A<int> verursacht eine Vorlage-Erweiterung von A<int> nicht.

Dies ist sinnvoll (für mich), da es das gleiche ist, wie einen Zeiger auf einen nach vorne deklarierten Typ zu erwähnen - Sie brauchen den Typ nicht vollständig an diesem Punkt definiert.

Vielleicht kann jemand, der weiser ist als ich, die Passage in der Norm finden, die dies vorschreibt.

0

Ich verstehe im Moment nicht, warum die enum hack hier nicht funktioniert. Aber das scheint zu funktionieren:

static const int v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))); 

Es kompiliert mit gcc-4.8.4 doch irgendwie Klirren-3.6 zu kompilieren. Der Fehler ist der gleiche wie zuvor.

Folgende compiliert mit den beiden:

template <class T> 
struct B { 
    struct U { }; 
    struct V { M& operator,(U); }; 
    static const int v; 
}; 

template <class T> 
const int B<T>::v = sizeof(M::m((R<V>::r(), R<A<T>*>::r())));