下面的程序可以用 g++(版本 10.1.0)编译,但不能用 clang++(10.0.0)编译
#include <iostream>
template <typename U>
struct A { U x; };
namespace tools {
template <typename U>
void operator+=(A<U>& lhs, const A<U>& rhs) { lhs.x += rhs.x; }
}
namespace impl {
template <typename U = int>
void f() {
A<U> a{3};
A<U> b{2};
a += b;
std::cout << a.x << std::endl;
}
}
namespace impl {
using namespace tools;
}
int main()
{
impl::f();
}
错误是:name.cpp:16:7: error: no viable overloaded '+='
a += b;
~ ^ ~
name.cpp:27:9: note: in instantiation of function template specialization 'impl::f<int>' requested here
impl::f();
显然,移动 using namespace tools
模板函数前的部分 impl::f()
消除了clang的错误。附加说明
这里重要的一点是
f
是一个模板函数。如果没有模板参数,代码既不能用 gcc 编译,也不能用 clang 编译。什么编译器在这里是正确的? gcc还是clang?
最佳答案
代码格式错误 因为不依赖于参数的非限定名称查找部分是在模板定义上下文中执行的。所以 Clang 是对的,并且已经报告了 GCC 错误( bug #70099 )
接下来是冗长的解释。
在您的示例代码中,有一些地方必须标记,以便讨论:
namespace impl {
template <typename U = int>
void f() { // (1) point of definition of the template f
A<U> a{3};
A<U> b{2};
a += b; // call operator += with arguments of dependent type A<U>
std::cout << a.x << std::endl;
}
}
namespace impl {
using namespace tools; // using directive
}
int main()
{
impl::f();
} // (2) point of instantiation of impl::f<int>
在模板定义处f
(1),对运算符 += 的调用使用类型 A<U>
的参数执行. A<U>
是 dependent type , 所以 operator +=
是 dependent name .[temp.dep.res]/1描述如何
operator +=
被查到:For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules from the template definition context ([basic.lookup.unqual], [basic.lookup.argdep]). [ Note: For the part of the lookup using associated namespaces ([basic.lookup.argdep]), function declarations found in the template instantiation context are found by this lookup, as described in [basic.lookup.argdep]. — end note ][...]
执行了两个查找。
非参数依赖的非限定名称查找 [basic.lookup.unqual] .
这种查找是从模板定义上下文中执行的。 “来自模板定义上下文”是指模板定义点的上下文。术语“上下文”指的是查找上下文。如模板
f
首次在命名空间 impl
中声明然后在全局命名空间范围中定义,不合格的名称查找仍然会找到命名空间 impl
的成员.这就是规则 [temp.dep.res]/1 使用“模板定义上下文”而不是简单的“模板定义点”的原因。此查找是从 (1) 执行的,但未找到
operator +=
在命名空间 tools
中定义. using 指令出现在 (1) 之后,并且无效。参数相关名称查找 (ADL) [basic.lookup.argdep]
ADL 在实例化点 (2) 执行。所以它是在 using 指令之后实现的。尽管如此,ADL 只考虑与参数类型关联的命名空间。参数类型为
A<int>
,模板A
是全局命名空间的成员,因此 ADL 只能找到该命名空间的成员。在 (2) 处没有
operator +=
在全局命名空间范围内声明。所以 ADL 也找不到 operator +=
的声明.
关于c++ - 错误 : no viable overloading with clang, 用 gcc 编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62530253/