2011-01-13 14 views
12

Ich habe einen Code gefunden, den ich denke sollte kompilieren, tut es aber nicht. Ich hoffe also, dass einige der lokalen Standardsxperten hier bei SO helfen können :-).Potentieller g ++ Template Bug?

Ich habe im Grunde einen Code, der dies ähnelt:

#include <iostream> 

template <class T = int> 
class A { 
public: 
    class U { 
    }; 

public: 
    U f() const { return U(); } 
}; 

// test either the work around or the code I want... 
#ifndef USE_FIX 
template <class T> 
bool operator==(const typename A<T>::U &x, int y) { 
    return true; 
} 
#else 
typedef A<int> AI; 
bool operator==(const AI::U &x, int y) { 
    return true; 
} 
#endif 

int main() { 
    A<int> a; 
    std::cout << (a.f() == 1) << std::endl; 
} 

Also, um zu beschreiben, was hier vor sich geht. Ich habe eine Klassenvorlage (A), die eine interne Klasse (U) und mindestens eine Memberfunktion hat, die eine Instanz dieser internen Klasse (f()) zurückgeben kann.

Dann versuche ich eine operator== Funktion zu erstellen, die diesen internen Typ mit einem anderen Typ vergleicht (in diesem Fall ein int, aber es scheint keine Rolle zu spielen).

Wenn USE_FIX ist nicht definiert bekomme ich folgende Fehlermeldung:

test.cc: In function 'int main()': 
test.cc:27:25: error: no match for 'operator==' in 'a.A<T>::f [with T = int]() == 1' 

Welche scheint seltsam, weil ich klar bin (glaube ich) ein Templat-operator== definiert, die diese in der Tat decken sollte, wenn ich nur Mach ein wenig von der Arbeit für den Compiler (aktiviere USE_FIX), dann bekomme ich keinen Fehler mehr. Leider funktioniert das "Fix" nicht generisch, nur für eine bestimmte Instanziierung der Vorlage.

Soll das so funktionieren, wie ich es erwartet habe? Oder ist das einfach nicht erlaubt?

BTW: Wenn es darauf ankommt, verwende ich gcc 4.5.2.

+1

Es ist einfach nicht erlaubt. Wenn ich mehr über Ihr Problem wissen würde, könnte ich Ihnen ein entsprechendes Redesign empfehlen. –

+0

+1 für eine gute Frage. :-) – Nawaz

+0

möglich duplicate of [Wie kann man den Klassentyp vom Methodentyp in C++ - Templates ableiten?] (Http://stackoverflow.com/questions/3830491/how-to-deduce-class-type-from-method-type-) in-c-templates) –

Antwort

15

Das Problem ist, dass const typename A<T>::U &xU eine abhängige Art und der Compiler kann T aus dem Argument nicht ableiten, (dies ist eine der nondeduced Kontext).

Sie könnten zum Beispiel haben zwei Spezialisierungen von A:

class X { }; 
class Y { }; 
class Z { }; 

template <> class A<X> { 
public: 
    typedef Z U; 
}; 

template <> class A<Y> { 
public: 
    typedef Z U; 
}; 

Wenn Sie rufen:

Z a; 
a == 1; 

was soll der Compiler ableiten T wie? X? Y?

Eine Lösung in diesem speziellen Fall ist innerhalb der Klassenvorlage operator== als nichtcodierende Freund zu erklären:

template <class T = int> 
class A { 
public: 
    class U { 
    }; 

    friend bool operator==(const U& x, int y) { 
     return true; 
    } 

public: 
    U f() const { return U(); } 
}; 
+0

+1 für eine gute Antwort auf eine gute Frage! – Nawaz

+0

@James: übrigens, 'Z' sollte innerhalb der Klassenvorlage deklariert werden, nur dann wären seine Situation und dein Beispiel zusammenpassen. Was sagst du? Ich meine, hier ist "Z" unabhängig von "A", während "U" in der Frage ein abhängiger Typ ist. – Nawaz

+3

@Nawaz: Es spielt keine Rolle, ob 'Z' abhängig ist; Es kommt darauf an, dass "U" abhängig ist. 'Z' kann alles sein. (Ich hatte ursprünglich 'int' verwendet, aber das war falsch, weil die Funktionsschablone aufgrund des eingebauten Operators nicht einmal bei der Überladungsauflösung in Betracht gezogen wurde. Daher führte ich' Z' als einen Typ ohne Nichtüberstreichen von 'operator ein == '). –

11
template <class T> 
bool operator==(const typename A<T>::U &x, int y) { 
    return true; 
} 

diese Vorlage verwenden, ist es nicht zulässig ist (oder manchmal möglich) die herzuleiten Vorlagenparameter T vom Typ x. Es handelt sich um einen nicht ableitbaren Kontext. (Zum Beispiel könnte jemand A für einen anderen Parameter spezialisiert, sagt double und A<double>::U ein typedef für A<int>::U machen.)

Es gibt keine Abhilfe, würden Sie explizit die Template-Parameter angeben müssen, die für operator== für hässliche Syntax macht.

+1

Ich denke diese Erklärung ist besser als James 'Erklärung. Präzise und präzise. +1 – Nawaz

4

Es ist aus naheliegenden Gründen nicht erlaubt. Im Allgemeinen gibt es wirklich keine Möglichkeit, dass der Compiler das Template-Argument von Ihrem Aufruf an den Operator == ableiten kann. Anscheinend haben Sie angenommen, dass der verschachtelte Typ U die einschließende A Spezialisierung eindeutig definiert. Das ist nicht wahr, was durch das folgende Beispiel mit zwei expliziten Spezialisierungen von A

template <> class A<int> { 
public: 
    class U {}; 
}; 

template <> class A<double> { 
public: 
    typedef A<int>::U U; 
}; 

In diesem Fall veranschaulicht werden kann, wenn man Operator Templat nennt == mit einem Argumente vom Typ A<int>::U die Compiler Template-Argument T für ableiten kann Vorlagenoperator ==. Sollte Tint oder double sein? Es gibt keine Möglichkeit zu sagen.

Um diese Mehrdeutigkeiten zu vermeiden, werden solche Situationen nicht-abgeleitete Kontexte genannt. Das Ableiten der umschließenden Klassenvorlagenargumente von einem verschachtelten Typ ist ein Beispiel für nicht abgeleiteten Kontext.

+0

+1 für eine gute Antwort auf eine gute Frage! – Nawaz