2015-11-27 12 views
10

mam ten przykład:Operator << na matrycy typu argumentu członkiem powoduje błąd tylko brzękiem

#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; 
} 

który z brzękiem ++ (3.6) daje: Wystąpił

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) { 

Brak błędów podczas g ++ - 4.8 z C++ 11 i g ++ - 5.2.1 z kompilacjami C++ 17. clang ++ - 3.6 potrzeby std::ostream &operator<<(std::ostream &os, const Type &t) do zdefiniowania przedA::foo<T>.

Z mojego punktu widzenia członek m_t zależy od typu argumentu szablonu i użyty operator<< dla tego typu nie powinien być potrzebny podczas definiowania szablonu. Dlaczego clang ma błąd kompilacji, a g ++ nie?

+0

Wygląda na to, że 14.7.3 [temp.expl.spec] pkt 7 (szczególnie ostatnie zdanie) ma zastosowanie. –

+2

@ DietmarKühl, ale tutaj nie ma wyraźnej specjalizacji, prawda? – TartanLlama

Odpowiedz

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

Spójrzmy na powiązane przestrzenie nazw tego typu. [Basic.lookup.argdep]/(2,2)

związany z nim nazw to najgłębsze zamykający nazw od związanych z nią grup.

To byłby obszar nazw std lub pomocniczy, ale z pewnością nie jest globalnym obszarem nazw.

Ponadto, jeśli T jest specjalizacja klasy szablon, jej towarzyszące nazw i ćwiczenia obejmują również: przestrzenie nazw i klas związane z typami argumentów szablonu przewidzianych parametrów typu szablonu (bez parametrów szablonu szablon) ; [... ...] przepisy niemające

Rekursywnie stosowania powyższego celu std::string daje nazw std (i znowu, ones pomocnicze) dla towarzyszących nazw. Na pewno nie globalna przestrzeń nazw. Oczywiście, tę samą argumentację można powtórzyć dla std::cout, podając ten sam wniosek.

Tak więc ADL nie będzie wyglądać w globalnej przestrzeni nazw, dokładnie tam, gdzie zadeklarowano twoje przeciążenie w.

Wreszcie zgodnie [temp.dep.candidate]/1, rozdzielczość nazwa jest nieskuteczne:

enter image description here

GCC zachowuje niezgodnych tutaj; patrz #51577.

+0

Wiem, że nie leży to w zakresie samego pytania - ale oprócz przeniesienia definicji operatora przed definicją szablonu, jakie byłoby najlepsze rozwiązanie tego problemu? – zoska

+0

@zoska Wyprowadź klasę globalną z 'std :: tuple ', dziedzicz konstruktory i używaj jej mniej więcej - jak byś użył typedef? – Columbo

+0

to jest to, co myślałem (i testowałem, ale wydaje się, że nigdy nie wiadomo, kiedy uderzę UB ...). Ale nigdy nie czuję się komfortowo, gdy dziedziczę z kontenerów STL, więc pomyślałem, że może jest lepsze rozwiązanie. Dzięki :) – zoska

Powiązane problemy