有效的现代C ++ 项目12 中,有一个有关 C ++ 11函数的引用限定符的示例代码:
class Widget {
public:
using DataType = std::vector<double>;
...
DataType &data() & // for lvalue Widgets
{ return values; } // return lvalue
DataType data() && // return rvalue Widgets
{ return std::move(values); } // return rvalue
...
private:
DataType values;
};
那么为什么第二个data()
右值引用重载函数返回临时对象 DataType
但不返回右值参考 DataType&&
?
答案 0 :(得分:0)
我看到的唯一原因是当对象是prvalue的结果时避免创建悬空引用:
Widget foo();
auto&& x = foo().data();
如果foo().data()
返回了对variable
成员的右值引用,则x
将是一个悬空引用,因为foo()
的结果对象在初始化结束时被破坏了的x
(完整表达式的结尾)。
另一方面,data()&&
按值返回,x
绑定到一个临时实现,该实现将与x
相同。因此避免了悬挂参考。
data() &&
的此返回类型在C ++中不是惯用的。通常,访问器函数返回一个引用,而上述用例可能会引发任何代码审阅者的“悬挂引用警报”。
这个data()&&
的定义很聪明,但是却违反了常规。
答案 1 :(得分:0)
方法后的 &
或 &&
有特殊用途:它们用作 ref value qualifier。
从某种意义上说,您可以放在函数的参数声明之后(即在参数的 )
之后)的所有内容意味着“仅当 *this
具有这些特性时才使用此方法”。< /p>
此处最典型的用法是 const
限定:仅在 const 上下文中调用覆盖:
class Widget {
void foo() {
cout << "Calling mutable foo\n";
}
void foo() const {
cout << "Calling const foo\n";
}
};
同样的事情可以用 &&
或 &
来完成,告诉编译器在 *this
有匹配的限定符时选择这些覆盖。
但是你为什么要这样做?
当您想区分以下之一时,这很有用:
*this
是右值引用的情况下复制数据并且不会超过调用代码的引用。一个让很多人厌烦的常见例子是在 for 循环中的使用。
考虑以下代码(基于您的 Widget
示例):
Widget buildWidget() { return Widget(); }
int main() {
for (auto i : buildWidget().data()) {
cout << i << '\n';
}
return 0;
}
在这种情况下,将调用 &&
覆盖,因为 Widget
返回的 buildWidget
对象没有名称,并且这是一个右值引用。
具体而言,一旦调用 data()
完成,底层的 Widget
对象(此处未命名)将立即死亡(它的生命周期将结束)。如果此 &&
覆盖不存在,则对其 data
成员的左值引用将指向已破坏的对象。 这将是未定义的行为。
一般来说,这个rvalue ref 限定的东西并不常见。只有在遇到这样的情况时才真正需要使用它(通常是当您想通过复制返回一个值时,*this
可能在调用后被破坏)。