C++ 转换构造函数的行为在 gcc 12 中发生了变化?

问题描述 投票:0回答:1

我观察到一种奇怪的行为,我无法解释。 以下代码(实际代码的简化版本)在 gcc 11.4 中正确编译,并且 MyStruct 的 read() 方法中的参数“val”使用转换构造函数隐式转换为 Dummy。

#include <iostream>

namespace secret_impl_space
{
  template <typename T>
  struct MyStruct
  {
    T val;
    MyStruct(T v): val(v) {}
    // the val below may be implicitly converted to Dummy if operator >> is missing from T
    virtual std::istream& read(std::istream& i) { i >> val; return i; }
  };

  struct Dummy { template<typename T> Dummy(const T& v){}; };

  inline std::istream& operator>>(std::istream& i, const Dummy& v)
  {
    std::cout << "Using Dummy" << std::endl; return i;
  }
}

struct A {int a;};

int main()
{
  A aa{1};
  secret_impl_space::MyStruct<A>* test (new secret_impl_space::MyStruct<A>(aa));
  return 0;
}

但是,我发现较新的 gcc 版本,从 12 开始,给我以下编译错误(用 godbolt 确认):

no match for ‘operator>>’ (operand types are ‘std::istream’ {aka ‘std::basic_istream<char>’} and ‘A’)

最奇怪的是,如果我执行以下两件事之一,代码可以在 any gcc 版本上正确编译:

  1. 摆脱名称空间“secret_impl_space”
  2. 从 read() 方法中删除虚拟说明符。

有人可以解释这种行为吗?老实说我很困惑。

注意:只是为了给读者一些背景信息,在原始代码中,MyStruct 是像 boost::any 这样的类型擦除容器的实现部分 - 这就是为什么它有一个虚拟 >> 方法,以重载类型中的方法- 删除了基本接口。定义 Dummy 类背后的整个想法是允许对某些没有 >> 运算符的类型使用类型擦除容器 - 生成运行时警告而不是编译器错误。恕我直言,这非常糟糕,但我没有写这个,当我发现问题时它已经存在了。由于某种原因(羞耻?),所有这些机器都“隐藏”在命名空间内。

c++ gcc namespaces implicit-conversion
1个回答
0
投票

无论如何,这应该不起作用。根据模板上下文中尚未声明匹配函数的函数调用的 ADL 规则(又名错误的 Koenig 规则),名称查找将推迟到实例化。

在实例化时,对于带有参数

std::basic_istream<char>
<global namespace>::A
的调用,仅应考虑命名空间
std
,考虑本地命名空间
secret_impl_space
或全局命名空间(其中定义了 A)将是不正确的。该错误列出了这些类型作为推理的方式。

这可能是修复 ADL 实施中的一些缺陷的结果。

代码也被 clang 拒绝。由于从全局命名空间开始的查找行为不合规,某些版本的 MSVC 可能会接受它。

© www.soinside.com 2019 - 2024. All rights reserved.