是否对const的rvalue引用有用吗?

fre*_*low 72 c++ const rvalue-reference c++11

我猜不是,但我想证实一下.是否有用const Foo&&,Foo哪种类型?

How*_*ant 69

它们偶尔会有用.草案C++ 0x本身在几个地方使用它们,例如:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Run Code Online (Sandbox Code Playgroud)

上面的两个重载确保了另一个ref(T&)cref(const T&)函数不绑定到rvalues(否则可能).

更新

我刚刚检查了官方标准N3290,遗憾的是它不公开,它有20.8个Function对象[function.objects]/p2:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Run Code Online (Sandbox Code Playgroud)

然后我检查了最新的C++ 11后草案,它是公开的,N3485,以及20.8函数对象[function.objects]/p2,它仍然说:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Run Code Online (Sandbox Code Playgroud)

  • 为什么三次在答案中包含相同的代码?我试图找到差异太久了. (39认同)
  • @ typ1232:由于评论中担心引用的功能不再出现,我似乎在我回答它近两年后更新了答案.我从N3290进行了复制/粘贴,并从当时最新的N3485草案中进行了复制/粘贴,以显示功能仍然出现.在我看来,使用复制/粘贴是确保比我的眼睛更多的眼睛可以确认我没有忽略这些签名中的一些微小变化的最佳方法. (8认同)
  • 验证两个片段等效性的过程:将第一个片段复制到文本文件中;将不相关的片段复制到第二个文本文件中;将第二个片段复制到第三个文本文件中。使用“diff”验证第一个和最后一个文件是否相同,以及第二个文件是否不同。 (3认同)
  • @OliverSeiler 不,你错了。转发引用是对 cv 不合格模板参数的右值引用。由于 const 限定符,该约束不是这里的情况,即它们的行为不像转发引用,而始终只是示例中对 T 的 const 右值引用。 (3认同)
  • 为什么“T&amp;&amp;”还不够? (2认同)
  • `const T&amp;&amp;` 的使用可以防止有人愚蠢地使用形式为 `ref&lt;const A&amp;&gt;(...)` 的显式模板参数。这不是一个非常有力的论点,但是 `const T&amp;&amp;` 相对于 `T&amp;&amp;` 的成本非常低。 (2认同)

Ami*_*rsh 12

获取const 右值引用(而不是 for =delete)的语义是说:

  • 我们不支持左值操作!
  • 尽管如此,我们仍然复制,因为我们无法移动传递的资源,或者因为“移动”它没有实际意义。

恕我直言,以下用例可能是对 const 右值引用的一个很好的用例,尽管语言决定不采用这种方法(请参阅原始 SO 帖子)。


案例:来自原始指针的智能指针构造函数

通常建议使用make_uniqueand make_shared,但是unique_ptrandshared_ptr都可以从原始指针构造。两个构造函数都按值获取指针并复制它。两者都允许(即从某种意义上说:不阻止)在构造函数中传递给它们的原始指针的继续使用。

以下代码编译并产生double free

int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Run Code Online (Sandbox Code Playgroud)

双方unique_ptrshared_ptr能防止上述,如果他们的相关构造期望得到原始指针作为一个const右值,例如用于unique_ptr

unique_ptr(T* const&& p) : ptr{p} {}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,上面的double free代码不会编译,但以下代码会:

std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) };     // ok, rvalue
Run Code Online (Sandbox Code Playgroud)

请注意,ptr移动后仍然可以使用,因此潜在的错误并没有完全消失。但是如果用户被要求调用std::move这样的 bug 就会落入常见的规则:不要使用被移动的资源。


有人会问:好的,但为什么是T* const&& p

原因很简单,允许unique_ptr 从 const 指针创建。请记住,const 右值引用比仅右值引用更通用,因为它同时接受constnon-const。所以我们可以允许以下内容:

int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
Run Code Online (Sandbox Code Playgroud)

如果我们只期望rvalue 引用(编译错误:无法将const rvalue绑定到rvalue),这将不会发生。


无论如何,现在提出这样的事情已经太晚了。但是这个想法确实提出了对 const右值引用的合理用法。


Gen*_*yev 5

它们是允许的,甚至函数排名基于const,但由于您无法从引用的 const 对象移动const Foo&&,因此它们没有用。

  • @FredOverflow,重载的排名是这样的:`const T&amp;, T&amp;, const T&amp;&amp;, T&amp;&amp;` (6认同)
  • @Fred:可变数据成员,或者可能为这种假设类型移动不需要修改数据成员。 (3认同)
  • @Fred:如何在不修改源的情况下移动? (2认同)

eer*_*ika 5

除了std::ref,标准库也出于同样的目的在std::as_const 中使用 const 右值引用。

template <class T>
void as_const(const T&&) = delete;
Run Code Online (Sandbox Code Playgroud)

获取包装值时,它还用作std::optional 中的返回值:

constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
Run Code Online (Sandbox Code Playgroud)

以及在std::get 中

template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
Run Code Online (Sandbox Code Playgroud)

这大概是为了在访问包装值时维护值类别以及包装器的常量性。

这对于是否可以在包装的对象上调用 const rvalue 引用限定的函数有所不同。也就是说,我不知道 const rvalue ref 限定函数的任何用途。