不使用std::forward如何调用成员变量的move构造函数?
How is the move constructor of member variable invoked without using std::forward?
一个例子 here 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]
提到
the fact that all named values (such as function parameters) always
evaluate as lvalues (even those declared as rvalue references)
然而,典型的移动构造函数看起来像
ClassName(ClassName&& other)
: _data(other._data)
{
}
看起来像 _data(other._data)
应该调用 _data
的 class 的移动构造函数。但是,不使用 std::forward
怎么可能呢?也就是说,不应该是
ClassName(ClassName&& other)
: _data(std::forward(other._data))
{
}
?
因为,正如 std:forward 案例中所指出的,
all then named values should evaluate as lvalue
我越来越喜欢 C++,因为像这样的问题很深,而且这种语言足够大胆,可以提供这样的功能 :) 谢谢!
一个典型的移动构造函数看起来像这样(假设它是显式实现的:你可能更喜欢 = default
):
ClassName::ClassName(ClassName&& other)
: _data(std::move(other._data)) {
}
没有 std::move()
成员被复制:因为它有一个名字 other
是一个左值。但是,引用绑定到的对象是右值或被视为右值的对象。
std::forward<T>(obj)
总是 与显式模板参数一起使用。实际上,该类型是为 转发引用 推导出来的。这些看起来非常像右值引用,但完全不同!特别是,转发引用可能引用左值。
您可能对我的 Two Daemons 文章感兴趣,其中详细描述了差异。
这个 Ideone example 应该让你很清楚了。如果没有,请继续阅读。
以下构造函数只接受右值。然而,由于参数 "other" 得到了一个名字,它失去了它的 "rvalueness",现在是一个左值。要将其转换回 Rvalue,您必须使用 std::move
。没有理由在这里使用 std::forward
因为这个构造函数不接受左值。如果你试图用左值调用它,你会得到编译错误。
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 将其称为 "Universal Rerefences",但现在称为 "Forwarding References"。这就是为什么在这里必须使用 std::forward
的原因,这样如果 other 是右值,_data 构造函数将使用右值调用。如果 other 是左值,则 _data 将使用左值构造。这就是为什么它被称为 perfect-forwarding.
template<typename T>
ClassName(T&& other)
: _data(std::forward<decltype(_data)>(other._data))
{
}
我尝试以您的构造函数为例,以便您理解,但这并非特定于构造函数。这也适用于函数。
对于第一个例子,因为你的第一个构造函数只接受右值,你可以完美地使用 std::forward
来代替,两者会做同样的事情。但最好不要这样做,因为人们可能会认为您的构造函数接受了 forwarding reference
,而实际上并没有。
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)
};
更多请查看this excellent in-depth article by Scott Meyers,注意写文章时使用了"universal reference"这个词(实际上是Scott自己介绍的)。现在一致认为 "forwarding reference" 更好地描述了它的目的和用法。
所以你的例子应该是:
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
}
因为 other
是一个 rvalue reference
因为 ClassName
不是推导类型。
一个例子 here 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]
提到
the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references)
然而,典型的移动构造函数看起来像
ClassName(ClassName&& other)
: _data(other._data)
{
}
看起来像 _data(other._data)
应该调用 _data
的 class 的移动构造函数。但是,不使用 std::forward
怎么可能呢?也就是说,不应该是
ClassName(ClassName&& other)
: _data(std::forward(other._data))
{
}
?
因为,正如 std:forward 案例中所指出的,
all then named values should evaluate as lvalue
我越来越喜欢 C++,因为像这样的问题很深,而且这种语言足够大胆,可以提供这样的功能 :) 谢谢!
一个典型的移动构造函数看起来像这样(假设它是显式实现的:你可能更喜欢 = default
):
ClassName::ClassName(ClassName&& other)
: _data(std::move(other._data)) {
}
没有 std::move()
成员被复制:因为它有一个名字 other
是一个左值。但是,引用绑定到的对象是右值或被视为右值的对象。
std::forward<T>(obj)
总是 与显式模板参数一起使用。实际上,该类型是为 转发引用 推导出来的。这些看起来非常像右值引用,但完全不同!特别是,转发引用可能引用左值。
您可能对我的 Two Daemons 文章感兴趣,其中详细描述了差异。
这个 Ideone example 应该让你很清楚了。如果没有,请继续阅读。
以下构造函数只接受右值。然而,由于参数 "other" 得到了一个名字,它失去了它的 "rvalueness",现在是一个左值。要将其转换回 Rvalue,您必须使用 std::move
。没有理由在这里使用 std::forward
因为这个构造函数不接受左值。如果你试图用左值调用它,你会得到编译错误。
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 将其称为 "Universal Rerefences",但现在称为 "Forwarding References"。这就是为什么在这里必须使用 std::forward
的原因,这样如果 other 是右值,_data 构造函数将使用右值调用。如果 other 是左值,则 _data 将使用左值构造。这就是为什么它被称为 perfect-forwarding.
template<typename T>
ClassName(T&& other)
: _data(std::forward<decltype(_data)>(other._data))
{
}
我尝试以您的构造函数为例,以便您理解,但这并非特定于构造函数。这也适用于函数。
对于第一个例子,因为你的第一个构造函数只接受右值,你可以完美地使用 std::forward
来代替,两者会做同样的事情。但最好不要这样做,因为人们可能会认为您的构造函数接受了 forwarding reference
,而实际上并没有。
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)
};
更多请查看this excellent in-depth article by Scott Meyers,注意写文章时使用了"universal reference"这个词(实际上是Scott自己介绍的)。现在一致认为 "forwarding reference" 更好地描述了它的目的和用法。
所以你的例子应该是:
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
}
因为 other
是一个 rvalue reference
因为 ClassName
不是推导类型。