我正在构建一个必须能够保存值或引用的模板类。 我正在构建 C++11 兼容性库。
这是该类的相关代码
template<typename T>
class Foo
{
private:
T _Data;
public:
Foo(const T& i_Data) :
_Data(i_Data)
{}
virtual ~Foo()
{}
Foo(const Foo &i_Other):
_Data(i_Other._Data)
{};
Foo(Foo &&io_Other):
_Data(std::move(io_Other._Data))
{}
// etc..
};
以这种方式创建 Foo 对象时,代码可以正常工作:
double Bar = 2.5;
Foo<double&> f = Bar; // This does not compile (in c++11, C++20 works though)
// error: cannot bind non-const lvalue reference of type ‘double&’ to an rvalue of type ‘std::remove_reference<double&>::type’ {aka ‘double’}
Foo<double&> f(Bar); // This does compile
我想,当使用第一行时,编译器尝试构建一个临时 Foo,然后将其移动到 Foo 中,尽管我不明白为什么以及如何干净地避免这种情况(当然,我可以删除移动构造函数但这是一个俗气的修复)
另外,为什么它适用于 C++17 而不是 C++11。据我所知,这不是与模板推导相关的问题,因此这在两个标准中应该是相同的,对吗?
在
Foo<double&>
中,采用 const T&
的构造函数将具有您可能没有预料到的类型,这是引用崩溃的结果。
您正在形成一个“对
double
的左值引用的const左值引用”,它会折叠成“对double
的引用”,因此您的构造函数实际上是:
Foo(double& i_Data)
因此,
Foo<double&> f = Bar;
...完全没问题,因为
Bar
是左值。
在 C++17 之前,在这种情况下没有强制复制省略,上面的行将隐式将
Bar
转换为 Foo<double&>
,然后使用移动构造函数,但您的移动构造函数不处理 T = double&
适当地。
编译器可以忽略此副本和对移动构造函数的调用,但至少在纸面上,该调用必须有效。
自 C++17 起,复制初始化只需直接调用
Foo(const T&)
,不会创建任何临时对象。