如何在不使用 std::forward 的情况下调用成员变量的移动构造函数
How is the move constructor of member variable invoked without using std::forward?
这里有一个std::forward
的例子,
// forward example
#include <utility> // std::forward
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}
// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
overloaded (x); // always an lvalue
overloaded (std::forward<T>(x)); // rvalue if argument is rvalue
}
int main () {
int a;
std::cout << "calling fn with lvalue: ";
fn (a);
std::cout << 'n';
std::cout << "calling fn with rvalue: ";
fn (0);
std::cout << 'n';
return 0;
}
Output:
calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]
提到
所有命名值(如函数参数(始终 评估为左值(即使是那些声明为右值引用(
然而,典型的移动构造函数看起来像
ClassName(ClassName&& other)
: _data(other._data)
{
}
看起来_data(other._data)
应该调用_data
类的移动构造函数。但是,不使用std::forward
怎么可能?换句话说,不应该是
ClassName(ClassName&& other)
: _data(std::forward(other._data))
{
}
?
因为,正如 std:forward case 中指出的那样,
所有命名值的计算结果应为左值
我越来越喜欢C++因为这样的问题的深度以及语言足够大胆以提供此类功能的事实:)谢谢!
典型的移动构造函数如下所示(假设它是显式实现的:您可能希望更喜欢= default
(:
ClassName::ClassName(ClassName&& other)
: _data(std::move(other._data)) {
}
如果没有std::move()
,则复制成员:因为它有一个名称other
是一个左值。但是,引用绑定到的对象是右值或被视为右值的对象。
std::forward<T>(obj)
始终与显式模板参数一起使用。实际上,该类型是为转发引用推断的类型。这些看起来非常像右值引用,但完全不同!特别是,转发引用可以引用左值。
您可能对我的两个守护进程文章感兴趣,该文章详细描述了差异。
std::forward
应该与forwarding reference
一起使用。
std::move
应与rvalue reference
一起使用。
构造函数没有什么特别的。这些规则同样适用于任何函数、成员函数或构造函数。
最重要的是意识到什么时候有forwarding reference
,什么时候有rvalue reference
。它们看起来很相似,但事实并非如此。
转发引用始终采用以下格式:
T&& ref
对于T
一些推断的类型。
例如,这是一个转发引用:
template <class T>
auto foo(T&& ref) -> void;
所有这些都是右值引用:
auto foo(int&& ref) -> void; // int not deduced
template <class T>
auto foo(const T&& ref); // not in form `T&&` (note the const)
template <class T>
auto foo(std::vector<T>&& ref) -> void; // not in form `T&&`
template <class T>
struct X {
auto foo(T&& ref) -> T; // T not deduced. (It was deduced at class level)
};
欲了解更多信息,请查看斯科特·迈耶斯(Scott Meyers(的这篇出色的深度文章,并注意在撰写文章时使用了"通用参考"一词(实际上是由斯科特本人介绍的(。现在人们一致认为,"转发引用"更好地描述了它的目的和用法。
所以你的例子应该是:
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
}
因为other
是一个rvalue reference
ClassName
因为不是推导类型。
这个 Ideone 示例应该会让你很清楚。如果没有,请继续阅读。
以下构造函数仅接受右值。然而,由于参数"其他"有一个名字,它失去了它的"右值",现在是一个左值。要将其转换回 Rvalue,您必须使用 std::move
.没有理由在这里使用 std::forward
,因为此构造函数不接受左值。如果您尝试使用Lvalue调用它,则会出现编译错误。
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
// If you don't use move, you could have:
// cout << other._data;
// And you will notice "other" has not been moved.
}
以下构造函数接受左值和右值。斯科特·迈耶斯(Scott Meyers(称之为"通用Rerefences",但现在它被称为"转发参考"。这就是为什么在这里必须使用 std::forward
,以便如果其他构造函数是 Rvalue,_data构造函数将使用 Rvalue 调用。如果 other 是左值,则_data将使用左值构造。这就是为什么它被称为perfect-forwarding.
template<typename T>
ClassName(T&& other)
: _data(std::forward<decltype(_data)>(other._data))
{
}
我尝试使用您的构造函数作为示例,以便您可以理解,但这不是特定于构造函数的。这也适用于函数。
对于第一个示例,由于您的第一个构造函数只接受 Rvalues,因此您可以完全使用 std::forward
,并且两者都会做同样的事情。但最好不要这样做,因为人们可能会认为您的构造函数接受forwarding reference
,而实际上并非如此。
- 在这种情况下,java对象是否可以调用本机函数
- 如果 std::vector::clear() 不是静态的,如何在没有实例的情况下调用它?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- Doxygen - 如何在不生成图形的情况下生成文本调用关系结果
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- C++:带有大括号初始化列表的函数调用表达式 - 标准是否规定在单个元素列表的微不足道的情况下忽略大括号?
- C++ - 如何在不调用其属性的情况下调用类?
- 有没有办法在没有虚拟的情况下使用基类指针调用派生类函数
- 为什么在这种情况下不调用我的虚拟函数实现?
- 在这种情况下,工会成员会调用自己的析构函数吗
- 如何在不迭代的情况下对数组中的每个元素调用方法
- C++-在没有自定义.lib文件的情况下从Lua C模块调用Lua函数
- 如何在派生类中不显式调用base::func()的情况下从基类执行虚拟函数
- C++ 在不释放内存的情况下调用析构函数
- Python在不引用类名的情况下调用类函数
- C++11 - 获取编译时的所有类变量,并在没有 Boost 的情况下为它们调用方法
- C ++:如何在不创建对象的情况下在主函数中调用方法
- 为什么在这种情况下调用非常量右值移动构造函数?
- 在这种情况下,我应该为每个WSASend调用传递唯一的重叠结构吗?
- C++ 在不知道子类型的情况下从父类型调用子方法