c++ - 错误 : no viable overloading with clang, 用 gcc 编译

标签 c++ templates namespaces language-lawyer

下面的程序可以用 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/

相关文章:

php - 使用 PHP 为 XML 标记设置命名空间

c# - 重命名命名空间后找不到为 Main 方法指定的 'WindowsFormsApplication1.Program'

C++ AMP 嵌套循环

c++ - 创建对话树

c++ - 是否有 c++ 特性可以在 C++ 中的两种类型之间找到最受限制的类型?

javascript - Meteor template.find 未定义

php - 拉维尔 : Class 'App\DB' not found

c++ - 链接 C++ 库 : Restbed

c++ - 出现在函数表达式中的变量是否通过引用获取参数但通过值 odr-used 返回?

C++11:通用执行器