【问题标题】:GCC and Clang template call resolution differencesGCC 和 Clang 模板调用解析差异
【发布时间】:2014-07-12 00:16:02
【问题描述】:

给定以下代码:

#include <iostream>

struct Alice
{
  template <typename A>
  void operator|(const A& /*a*/) const
  {
    std::cout << "operator| member" << std::endl;
  }
};

template <typename A>
void operator|(const A& /*a*/, const Alice& /*alice*/)
{
  std::cout << "operator| non-member" << std::endl;
}

int main()
{
  Alice a;
  Alice b;

  a | b;

  return 0;
}

使用 GCC 4.8.1、4.9 和 clang 3.4 编译时不会发出警告,但会给出不同的结果。

$ g++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| non-member

$ clang++ -Wall -Wextra -std=c++11 alice.cpp && ./a.out
operator| member

造成这种差异的原因是什么?如何强制执行相同的行为?

编辑: 有趣的事实:从成员函数中删除 const 限定符使 gcc 也更喜欢成员函数。然而,它并没有解决问题。

编辑:如果未指定-std=c++11,则clang++ 更喜欢非成员。

编辑: ICC 14.0 更喜欢非会员,不会发出警告。

【问题讨论】:

  • Visual Studio 2013,就其本身而言,errors out 带有“operator| is ambiguous”。
  • gcc 4.9.0 返回与 gcc 4.8.1 相同的结果。
  • 据我所知,这些重载应该都一样好,因此应该因为模棱两可而被拒绝。您是否检查过各种错误跟踪器,可能是已知问题?
  • 我不能 reproduce 你在 clang 中看到的行为。在 C++03 模式下编译,clang 的行为与 gcc 相同,但在 C++11 模式下编译,它会出错并显示操作员调用不明确的消息。
  • 注意有一个相关的clang bug。但是,找不到任何关于 g++ 的信息。

标签: c++ gcc clang language-lawyer


【解决方案1】:

根据重载决议,有两个可行的函数:具有推导参数的全局operator|-模板的特化和成员运算符函数模板的特化。两者具有相同的签名 - 成员函数模板具有 Alice const&amp; 类型的隐式对象参数(请参阅 §13.3.1/4)。

所以两个可行的函数(在模板参数推导之后)具有相同的签名。并且它们被实例化的模板都没有比另一个更专业。所以这确实是一个模棱两可的,因此是不正确的。令人惊讶的是,VC++ 是正确的。

如何强制执行相同的行为?

也许你应该去掉歧义,然后 Clang、VC++ 和 GCC 应该有相同的行为。

【讨论】:

  • 感谢您总结情况。这确实只是一个示例,以证明编译器行为不端。原始代码要复杂得多:github.com/erenon/pipeline(但是,错误代码没有提交,但可以理解我为什么要摆弄 operator|s)
  • 它已经“更正”了。 (或者更确切地说,它可以按我的意愿工作)
猜你喜欢
  • 2012-08-14
  • 2021-07-06
  • 1970-01-01
  • 2017-10-09
  • 2020-01-17
  • 2019-03-26
  • 2019-06-05
  • 1970-01-01
  • 2020-01-18
相关资源
最近更新 更多