2015-11-27 4 views
10

ich dieses Beispiel haben:Operator << auf Template-Argument Typ verursacht Mitglied Fehler nur in Klirren

#include <iostream> 
#include <tuple> 
#include <string> 

template <typename T> 
class A { 
public: 
    A(const T &t) : m_t(t) {} 
    void foo() { 
     std::cout << m_t << std::endl; 
    } 

private: 
    T m_t; 
}; 

typedef std::tuple<std::string, std::string> Type; 
std::ostream &operator<<(std::ostream &os, const Type &t) { 
    os << std::get<0>(t) << " " << std::get<1>(t); 
    return os; 
} 

int main() { 
    A<Type> a(Type{"ala", " ma kota"}); 
    a.foo(); 
    return 0; 
} 

, die mit Klappern ++ (3.6) erzeugt:

test_clang.cpp:10:19: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup 
     std::cout << m_t << std::endl; 
      ^
test_clang.cpp:26:7: note: in instantiation of member function 'A<std::tuple<std::basic_string<char>, std::basic_string<char> > >::foo' requested here 
    a.foo(); 
    ^
test_clang.cpp:19:15: note: 'operator<<' should be declared prior to the call site 
std::ostream &operator<<(std::ostream &os, const Type &t) { 

Keine Fehler bei g ++ aufgetreten - 4.8 mit C++ 11 und g ++ - 5.2.1 mit C++ 17 Builds. clang ++ - 3.6 muss std::ostream &operator<<(std::ostream &os, const Type &t) definiert werden vorA::foo<T>.

Aus meiner Perspektive Mitglied m_t hängt von Vorlage Argumenttyp und verwendet operator<< für diesen Typ sollte nicht während der Template-Definition benötigt werden. Warum hat call compilation error und g ++ nicht?

+0

Es scheint, dass 14.7.3 [temp.expl.spec] Absatz 7 (vor allem der letzte Satz) gilt. –

+2

@ DietmarKühl aber hier gibt es keine explizite Spezialisierung, oder? – TartanLlama

Antwort

1
std::tuple<std::string, std::string> 

Schauen wir uns die zugehörigen Namespaces dieses Typs an. [Basic.lookup.argdep]/(2.2):

Der zugehörige Namensraum sind die innersten Namensräume seiner zugehörigen Klassen umschließt.

Das wäre Namespace std oder zusätzliche, aber sicherlich nicht der globale Namespace.

Wenn ferner T eine Klasse Vorlage Spezialisierung ist, seine zugehörige Namensräume und Klassen umfassen auch: die Namensräume und Klassen Zusammenhang mit den Arten der Vorlage Argumente für Vorlagenart Parameter vorgesehen (ohne Vorlage Vorlage Parameter) ; [... unanwendbar Regeln ...]

Recursively die oben std::string Anwendung gibt Namespace std (und wiederum Hilfs sind) für die zugehörigen Namensraum. Sicher nicht der globale Namensraum. Es ist klar, dass dieselbe Argumentation für std::cout wiederholt werden kann, was die gleiche Schlussfolgerung ergibt.

So ADL wird nicht in den globalen Namespace suchen, wo genau Ihre Überlastung in deklariert ist.

schließlich nach [temp.dep.candidate]/1, Namensauflösung ist nicht erfolgreich:

enter image description here

GCC verhält sich nicht konforme hier; siehe #51577.

+0

Ich weiß, dass es nicht im Bereich der Frage selbst ist - aber abgesehen von der Definition des Operators vor der Definition der Vorlage, was wäre die beste Lösung, um es zu beheben? – zoska

+0

@zoska Leiten Sie eine globale Klasse aus 'std :: tuple ' her, erben Konstruktoren und benutzen Sie sie mehr oder weniger - so wie Sie einen typedef verwenden würden? – Columbo

+0

das ist was ich dachte (und getestet, aber scheint, ich weiß nie, wann ich UB traf ...). Aber ich fühle mich nie wohl bei der Übernahme von STL-Containern, also dachte ich, dass es vielleicht eine bessere Lösung gibt. Danke :) – zoska