运算符<< on template argument type member仅在clang中导致错误

zos*_*ska 10 c++ templates g++ language-lawyer clang++

我有这个例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

与clang ++(3.6)生成:

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) {
Run Code Online (Sandbox Code Playgroud)

使用C++ 11和g ++ - 5.2.1使用C++ 17版本的g ++ - 4.8期间没有发生错误.铛++ - 3.6需求std::ostream &operator<<(std::ostream &os, const Type &t)被定义之前 A::foo<T>.

从我的观点来看,成员m_t依赖于模板参数类型,并且operator<<在模板定义期间不应该使用此类型.为什么clang有编译错误而g ++没有?

Col*_*mbo 2

std::tuple<std::string, std::string>\n
Run Code Online (Sandbox Code Playgroud)\n\n

让我们看看这种类型的关联命名空间。[basic.lookup.argdep]/(2.2):

\n\n
\n

它的关联命名空间是其关联类的最内层封闭命名空间。

\n
\n\n

那将是命名空间std或辅助命名空间,但肯定不是全局命名空间。

\n\n
\n

此外,如果T是类模板特化,则其关联的命名空间和类还包括:与为模板类型参数(不包括模板模板参数)提供的模板实参的类型关联的命名空间和类;[\xe2\x80\xa6 不适用规则\xe2\x80\xa6]

\n
\n\n

递归地应用上面的内容来为关联的命名空间std::string提供命名空间std(以及辅助命名空间)。当然不是全局命名空间。显然,可以对 重复相同的论证std::cout,得出相同的结论。

\n\n

因此,ADL 不会在全局命名空间中查找,而这正是在.

\n\n

最后,根据 [temp.dep.candidate]/1,名称解析不成功:

\n\n

在此输入图像描述

\n\n

GCC 在这里表现得不合规;请参阅#51577

\n