Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operator << on template argument type member causes error only in clang

I have this example:

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

which with clang++ (3.6) produces:

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

No errors occurred during g++-4.8 with C++11 and g++-5.2.1 with C++17 builds. clang++-3.6 needs std::ostream &operator<<(std::ostream &os, const Type &t) to be defined before A::foo<T>.

From my perspective member m_t depends on template argument type and used operator<< for this type shouldn't be needed during template definition. Why clang has compilation error and g++ doesn't?

like image 303
zoska Avatar asked Nov 27 '15 14:11

zoska


1 Answers

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

Let's look at the associated namespaces of this type. [basic.lookup.argdep]/(2.2):

Its associated namespaces are the innermost enclosing namespaces of its associated classes.

That would be namespace std or auxiliary ones, but certainly not the global namespace.

Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); [… inapplicable rules…]

Recursively applying the above to std::string gives namespace std (and, again, auxiliary ones) for the associated namespaces. Certainly not the global namespace. Clearly, the same argumentation can be repeated for std::cout, giving the same conclusion.

Thus ADL won't look in the global namespace, which is precisely where your overload is declared in.

Finally, as per [temp.dep.candidate]/1, name resolution is unsuccessful:

enter image description here

GCC behaves non-conforming here; see #51577.

like image 57
Columbo Avatar answered Nov 10 '22 01:11

Columbo