C++为什么将左值传递给move构造函数对模板有效
C++ why does passing an lvalue to a move constructor work for templates?
我有这段代码,它没有编译,这是意料之中的事
这是错误:an rvalue reference cannot be bound to an lvalue
class SomeData
{
public:
vector<int> data;
SomeData()
{
cout << "SomeData ctor" << endl;
data.push_back(1);
data.push_back(2);
data.push_back(3);
}
SomeData(const SomeData &other)
{
cout << "SomeData copy ctor" << endl;
data = other.data;
}
SomeData(SomeData &&other)
{
cout << "SomeData move ctor" << endl;
data = move(other.data);
}
~SomeData()
{
cout << "SomeData dtor" << endl;
}
void Print() const
{
for(int i : data)
cout << i;
cout << endl;
}
};
void Function(SomeData &&someData)
{
SomeData localData(someData);
localData.Print();
}
int main(int argc, char *argv[])
{
SomeData data;
Function(data); // ERROR
data.Print();
return 0;
}
但是,当我将Function()
转换为模板时,它工作得很好,而是使用SomeData
的复制构造函数。
template<class T>
void Function(T &&someData)
{
T localData(someData); // no more error
localData.Print();
}
这是标准的C++行为吗?
我注意到visualstudio在模板方面往往更宽容,所以我想知道是否所有兼容的C++11编译器都会有同样的行为。
是。在模板函数的情况下,编译器推导模板参数T
,使其与给定的参数匹配。
由于someData
实际上是一个左值,因此T
被推导为SomeData &
。Function
的声明,经过类型推导,然后变成
void Function(SomeData & &&)
并且SomeData & &&
,遵循参考折叠的规则,变为SomeData &
。
因此,函数自变量someData
变为左值引用,并按原样传递给localData
的初始化。注意(正如@aschepler正确指出的)localData
被声明为T
,因此它本身就是SomeData &
类型的引用。因此,这里不会发生复制构造–只是引用的初始化。
如果希望localData
是实际副本,则必须将类型从SomeData &
转换为SomeData
,即必须从类型中删除&
。您可以使用std::remove_reference
:来完成此操作
template<class T>
void Function(T &&someData)
{
/* Create a copy, rather than initialize a reference: */
typename std::remove_reference<T>::type localData(someData);
localData.Print();
}
(为此,您必须使用#include <type_traits>
。)
这确实是预期行为。表单的模板功能:
template< class T >
Ret Function( T&& param )
{
...
}
遵循特殊规则(Ret可以是模板,也可以不是模板,这无关紧要)。T&;被称为通用引用,它基本上可以绑定到任何东西。这是因为当模板推导开始并且参数处于该形式时(注意,vector<T>&&
不是通用引用,C<T>
也不是任何其他模板参数的通用引用),将应用引用折叠规则:
T=int&=>T&;=int&&并且塌陷为单个int&
完整的corrispondence表是:
& + && = &
&& + & = &
&& + && = &&
所以当你有上述功能
int a = 5;
int b& = a;
Function( a ); // (1)
Function( b ); // (2)
Function( 3 ); // (3)
在情况1中,T=int&并且推导出的类型是int&(由于a是左值),因此函数具有以下签名:
Ret Function( int& param ) // int& && collapses to int&
在情况2中,T=int&
Ret Function( int& param ) // int& && collapses to int&
在情况3中,T=int
Ret Function( int&& param )
这个崩溃的规则是委员会发现的合理的,以使完美的转发工作。你可以在Scott Meyers的视频中找到长话短说
只是为了让您放心:这不是MSVC问题,这实际上是预期行为。
在模板中,&&
在应用于模板类型参数时具有不同的含义。它被称为通用引用。
我可能会转述一下,但这篇文章解释得很好,所以你应该读一读
概括起来(而且非常不精确),如果需要,通用引用可以"衰减"为普通引用。
- 在 c++ 中将对象设置为等于同一类的构造函数是否有效?
- 我应该将哪种有效负载类型发送给webrtc::P ayloadRouter的构造函数?
- 为什么构造函数的虚拟函数调用有时有效,但其他调用却无效
- 为什么即使直接构造函数有效,template_back也会失败
- 提供初始值设定项列表构造函数的有效方法
- 让构造函数在其初始化列表中调用同一类的另一个构造函数是否有效
- 字符串或const char*,更有效地用作构造函数参数
- 字符串构造函数将两个字符* 放入另一个 std::string 在 C++14 中有效,但不适用于 C++17
- 以 std::p air 作为参数的构造函数:T a({1,2}) 有效,T a = {1,2} 不工作
- 没有参数的构造函数无法正常工作,但有效
- 调用不带参数的构造函数有效,使用参数则无效。为什么?
- 具有未初始化成员的结构的constexpr默认构造函数仅在模板化时有效
- std::使用了move,调用了move构造函数,但对象仍然有效
- C++ 为什么这些构造函数调用中有 3 个有效,但有一个不起作用?
- 检测默认构造函数是否有效
- C++:如果所有数据可能都已有效,则在构造函数中进行验证是否有开销
- 在构造函数后初始化零向量的有效方法
- 如何处理传递给构造函数的语法有效但逻辑无效的参数
- 在派生类构造函数中复制继承的成员有效吗
- 当我们进行复制初始化时,复制构造函数或构造函数是否有效