我观察到一种奇怪的行为,我无法解释。以下代码(实际代码的简化版本)在 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’)
最奇怪的是,如果我执行以下两件事之一,代码可以在任何gcc 版本上正确编译:
- 删除命名空间“secret_impl_space”
- 从 read() 方法中删除虚拟说明符。
有人能解释一下这种现象吗?我真的很困惑。
注意:只是为了给读者一些背景信息,在原始代码中,MyStruct 是类型擦除容器(如 boost::any)的实现部分 - 这就是为什么它有一个虚拟 >> 方法,用于重载类型擦除基接口中的方法。定义 Dummy 类的整个想法是允许将类型擦除容器也用于某些没有 >> 运算符的类型 - 生成运行时警告而不是编译器错误。在我看来这非常糟糕,但我没有写这个,当我发现问题时它已经存在了。出于某种原因(羞耻?),所有这些机制都“隐藏”在命名空间内。