在 && 限定函数中返回右值引用和值之间的区别
Difference between returning an rvalue reference and a value in && qualified functions
最近看到这个问题Is returning by rvalue reference more efficient?,也注意到了评论链。我正在跟进评论链以获取那里的答案。
人们似乎说在下面的情况下 return 值应该是右值引用而不是值。 (Is returning by rvalue reference more efficient? 和 RValue Reference (&&) 的 return 是否有用?)给定以下代码
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
};
class Maker {
public:
Something get_something() && {
return std::move(this->something);
}
const Something& get_something() & {
return this->something;
}
private:
Something something;
};
int main() {
auto maker = Maker{};
auto something_one = maker.get_something();
auto something_two = Maker{}.get_something();
return 0;
}
在 class Maker
中用右值引用 return 类型和常规值定义第一个方法有什么区别? 运行 上面的代码按预期给出了以下输出(移动发生在函数调用中并且在 return 之后移动被省略)
Something()
Something(const Something&)
Something()
Something(Something&&)
并且当我将代码(即 Maker
class 中的第一个方法)更改为 return 右值引用时,我仍然得到相同的输出。在那种情况下,我为什么不按值 return 呢?
当您以这种方式使用 this
引用限定符时,您必须问自己两个问题:
std::move(object).funcname()
是什么意思?
Typename().funcname()
是什么意思?
如果您 return 函数中的一个值,那么这两个值将意味着相同的事情。无论您如何捕获该值,该值都将是一个完整且不同的对象,由存储在该对象中的一些内部数据移动构造而成。
在第一种情况下,object
现在可能不再拥有数据。在第二种情况下,这无关紧要,因为该对象是临时对象并且已被销毁。
如果你从函数中 return 一个 &&
,那么它们将意味着不同的东西。即,#2 表示 "your code is broken".
至于为什么你可能仍然想这样做,即使它允许破坏代码。好吧,这与该问题的实际答案有关:这些东西是什么意思 ?
这就是我所指的。 std::get
基本上是 tuple
的成员函数。然而,如果您将 tuple&&
传递给它,您将得到 T&&
returned 而不是 T
。为什么?
因为您正在访问 tuple
的 成员。
如果你有一个 struct
,那么 std::move(struct_object).x
也将是一个右值引用。所以 std::get
对 tuple
的行为方式与对 struct
的成员访问的行为方式相同。毕竟,这就是 tuple
的全部意义:尽可能表现得像 struct
。
这允许你做类似 std::get<0>(std::move(tpl)).member
的事情,这样 member
仍然是一个右值引用。因此,您可以在不干扰对象的 rest 的情况下从子对象移动,就像您对任何其他右值引用成员访问所做的那样。
如果get
return编辑了一个值,那么这将做一些非常不同的事情。无论我们如何处理 return 值,它都会被移出对象,没有任何问题。因此,如果我们只想移动该成员的子对象……太糟糕了。原始对象丢失了整个成员,而不仅仅是该成员的子对象。
当然,这不会改变以下事实:
auto &&x = SomeStruct().x; //This extends the temporary's lifetime
auto &&x = std::get<0>(SomeTuple(...)); //This gets a dangling reference.
不幸的是,这是语言的局限性。但是如果该函数在逻辑上是一个成员访问器,那么 return 从一个 &&
限定的 this
函数而不是一个值中 &&
是一个合法的选择。
这完全取决于什么对您更重要:安全性或与成员访问器的正交性。
最近看到这个问题Is returning by rvalue reference more efficient?,也注意到了评论链。我正在跟进评论链以获取那里的答案。
人们似乎说在下面的情况下 return 值应该是右值引用而不是值。 (Is returning by rvalue reference more efficient? 和 RValue Reference (&&) 的 return 是否有用?)给定以下代码
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
};
class Maker {
public:
Something get_something() && {
return std::move(this->something);
}
const Something& get_something() & {
return this->something;
}
private:
Something something;
};
int main() {
auto maker = Maker{};
auto something_one = maker.get_something();
auto something_two = Maker{}.get_something();
return 0;
}
在 class Maker
中用右值引用 return 类型和常规值定义第一个方法有什么区别? 运行 上面的代码按预期给出了以下输出(移动发生在函数调用中并且在 return 之后移动被省略)
Something()
Something(const Something&)
Something()
Something(Something&&)
并且当我将代码(即 Maker
class 中的第一个方法)更改为 return 右值引用时,我仍然得到相同的输出。在那种情况下,我为什么不按值 return 呢?
当您以这种方式使用 this
引用限定符时,您必须问自己两个问题:
std::move(object).funcname()
是什么意思?Typename().funcname()
是什么意思?
如果您 return 函数中的一个值,那么这两个值将意味着相同的事情。无论您如何捕获该值,该值都将是一个完整且不同的对象,由存储在该对象中的一些内部数据移动构造而成。
在第一种情况下,object
现在可能不再拥有数据。在第二种情况下,这无关紧要,因为该对象是临时对象并且已被销毁。
如果你从函数中 return 一个 &&
,那么它们将意味着不同的东西。即,#2 表示 "your code is broken".
至于为什么你可能仍然想这样做,即使它允许破坏代码。好吧,这与该问题的实际答案有关:这些东西是什么意思 ?
这就是我所指的。 std::get
基本上是 tuple
的成员函数。然而,如果您将 tuple&&
传递给它,您将得到 T&&
returned 而不是 T
。为什么?
因为您正在访问 tuple
的 成员。
如果你有一个 struct
,那么 std::move(struct_object).x
也将是一个右值引用。所以 std::get
对 tuple
的行为方式与对 struct
的成员访问的行为方式相同。毕竟,这就是 tuple
的全部意义:尽可能表现得像 struct
。
这允许你做类似 std::get<0>(std::move(tpl)).member
的事情,这样 member
仍然是一个右值引用。因此,您可以在不干扰对象的 rest 的情况下从子对象移动,就像您对任何其他右值引用成员访问所做的那样。
如果get
return编辑了一个值,那么这将做一些非常不同的事情。无论我们如何处理 return 值,它都会被移出对象,没有任何问题。因此,如果我们只想移动该成员的子对象……太糟糕了。原始对象丢失了整个成员,而不仅仅是该成员的子对象。
当然,这不会改变以下事实:
auto &&x = SomeStruct().x; //This extends the temporary's lifetime
auto &&x = std::get<0>(SomeTuple(...)); //This gets a dangling reference.
不幸的是,这是语言的局限性。但是如果该函数在逻辑上是一个成员访问器,那么 return 从一个 &&
限定的 this
函数而不是一个值中 &&
是一个合法的选择。
这完全取决于什么对您更重要:安全性或与成员访问器的正交性。