我不知道哪个 C++ 标准提供了此功能,但我想不出此功能的任何用例。
如果对象是右值,则仅通过重载解析来考虑带有&&修饰符的成员函数
struct Foo
{
auto func() && {...}
};
auto a = Foo{};
a.func(); // Does not compile, 'a' is not an rvalue
std::move(a).func(); // Compiles
Foo{}.func(); // Compiles
Run Code Online (Sandbox Code Playgroud)
有人可以解释一下这个的用例吗?
为什么我们希望只对右值执行某些例程?
c++11中添加了引用限定。一般来说,值类别的传播对于泛型编程非常有用!
从语义上讲,函数的引用限定有助于传达意图;作用于左值引用或右值——这类似于函数的const限定volatile。这些也与结构成员的行为平行,结构成员传播限定符和类别。
实践中一个很好的例子是std::optional,它提供了std::optional::value()将值类别传播到提取时的引用的函数:
auto x = std::move(opt).value(); // retrieves a T&&
Run Code Online (Sandbox Code Playgroud)
这类似于 s 的成员访问struct,其中值类别在访问时传播:
struct Data {
std::string value;
};
auto data = Data{};
auto string = std::move(data).value; // expression yields a std::string&&
Run Code Online (Sandbox Code Playgroud)
就通用组合而言,这极大地简化了输入可能是左值或右值的情况。例如,考虑使用转发引用的情况:
struct Data {
std::string value;
};
auto data = Data{};
auto string = std::move(data).value; // expression yields a std::string&&
Run Code Online (Sandbox Code Playgroud)
如果没有引用限定,完成上述代码的唯一方法是创建两个静态分支——或者使用if constexpr标签调度,或者使用其他方式。就像是:
// Gets the internal value from 'optional'
template <typename Optional>
auto call(Optional&& opt) {
// will be lvalue or rvalue depending on what 'opt' resolves as
return std::forward<Optional>(opt).value();
}
Run Code Online (Sandbox Code Playgroud)
在技术层面上,函数的右值限定提供了通过移动构造优化代码并以语义清晰的方式避免复制的机会。
就像当你看到一个std::move(x)值时,你会预期它x即将过期;期望这std::move(x).get_something()会导致x做同样的事情并不是没有道理的。
如果将&&重载与const &重载结合起来,则可以在 API 中表示不可变复制和变异移动。以不起眼的“Builder”模式为例。通常,构建器模式对象保存将在构造时输入到对象中的数据片段。这就需要在构建过程中进行浅拷贝或深拷贝。对于大型对象,这可能会非常昂贵:
template <typename Optional>
auto call(Optional&& opt) {
if constexpr (std::is_lvalue_reference_v<Optional>) {
return opt.value();
} else {
return std::move(opt.value());
}
}
Run Code Online (Sandbox Code Playgroud)
如果没有右值限定,您将被迫在实现上做出选择:
const函数中移动,并仅记录安全性(并希望 API 不会调用错误),或者有了右值限定,它就成为调用者的一个可选功能,并且从编写的代码中可以清楚地看出其意图是什么——无需文档:
class Builder {
private:
// Will be copied during construction
expensive_data m_expensive_state;
...
public:
auto add_expensive_data(...) -> Builder&;
auto add_other_data(...) -> Builder&;
...
auto build() && -> ExpensiveObject {
// Move the expensive-state, which is cheaper.
return ExpensiveObject{std::move(m_expensive_state), ...}
}
auto build() const & -> ExpensiveObject
// Copies the expensive-state, whcih is costly
return ExpensiveObject{m_expensive_state, ...}
}
...
};
Run Code Online (Sandbox Code Playgroud)
作为一个额外的好处,静态分析通常可以检测 use-after-move 情况,因此与简单的文档相比,可以更好地捕获builderafter- 的意外使用。std::move