模板参数类型成员上的运算符 << 仅在 clang 中导致错误
Operator << on template argument type member causes error only in 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;
}
用 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) {
在使用 C++11 的 g++-4.8 和使用 C++17 的 g++-5.2.1 期间没有发生错误。 clang++-3.6 需要在A::foo<T>
.
之前定义std::ostream &operator<<(std::ostream &os, const Type &t)
从我的角度来看,成员 m_t
取决于模板参数类型,并且在模板定义期间不应需要使用 operator<<
用于此类型。为什么clang有编译错误而g++没有?
std::tuple<std::string, std::string>
让我们看看这种类型的关联命名空间。 [basic.lookup.argdep]/(2.2):
Its associated namespaces are the
innermost enclosing namespaces of its associated classes.
那将是命名空间 std
或辅助命名空间,但肯定不是全局命名空间。
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…]
递归地将上述应用到 std::string
为关联的名称空间提供名称空间 std
(以及辅助名称空间)。当然不是全局命名空间。显然,可以对 std::cout
重复相同的论证,得出相同的结论。
因此 ADL 不会在全局命名空间中查找,这正是您在 中声明重载的位置。
最后,按照[temp.dep.candidate]/1,名称解析不成功:
GCC 在这里表现不一致;参见 #51577。
我有这个例子:
#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;
}
用 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) {
在使用 C++11 的 g++-4.8 和使用 C++17 的 g++-5.2.1 期间没有发生错误。 clang++-3.6 需要在A::foo<T>
.
std::ostream &operator<<(std::ostream &os, const Type &t)
从我的角度来看,成员 m_t
取决于模板参数类型,并且在模板定义期间不应需要使用 operator<<
用于此类型。为什么clang有编译错误而g++没有?
std::tuple<std::string, std::string>
让我们看看这种类型的关联命名空间。 [basic.lookup.argdep]/(2.2):
Its associated namespaces are the innermost enclosing namespaces of its associated classes.
那将是命名空间 std
或辅助命名空间,但肯定不是全局命名空间。
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…]
递归地将上述应用到 std::string
为关联的名称空间提供名称空间 std
(以及辅助名称空间)。当然不是全局命名空间。显然,可以对 std::cout
重复相同的论证,得出相同的结论。
因此 ADL 不会在全局命名空间中查找,这正是您在 中声明重载的位置。
最后,按照[temp.dep.candidate]/1,名称解析不成功:
GCC 在这里表现不一致;参见 #51577。