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

时间:2011-02-08 21:49:03

标签: c++ c++11 const rvalue-reference

我猜不是,但我想证实一下。 const Foo&&是否有用,其中Foo是类类型?

5 个答案:

答案 0 :(得分:65)

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

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

以上两个重载确保其他ref(T&)cref(const T&)函数不绑定到rvalues(否则可能)。

<强>更新

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

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

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

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

答案 1 :(得分:6)

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

  • 我们不支持左值操作!
  • 即使,我们仍会复制,因为我们不能移动所传递的资源,或者因为“移动”该资源没有实际意义。

以下用例可能是恕我直言是右值引用const 的好用例,尽管该语言决定不采用这种方法(请参见{{3} }。


案例:原始指针的智能指针构造器

通常建议使用make_uniquemake_shared,但是unique_ptrshared_ptr都可以由原始指针构造。两个构造函数都按值获取指针并将其复制。两者都允许(即:不阻止)继续使用构造函数中传递给它们的原始指针。

以下代码使用 double free 编译并生成结果:

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

如果unique_ptrshared_ptr的相关构造函数希望将原始指针作为常量值,例如它们,则可以防止上述情况发生为unique_ptr

unique_ptr(T* const&& p) : ptr{p} {}

在这种情况下,上面的 双重免费 代码无法编译,但以下代码可以:

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

请注意,ptr在移动后仍可以使用,因此潜在的错误并未完全消失。但是,如果要求用户调用std::move,则该错误会归入以下常见规则:不要使用已移动的资源。


一个人可以问:好,但是为什么要T* const && p

原因很简单,允许从const指针 创建unique_ptr 。请记住,常量右值引用右值引用更通用,因为它接受constnon-const。因此,我们可以允许以下操作:

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

如果我们仅希望 rvalue引用(编译错误:无法将 const rvalue 绑定到 rvalue ),则不会成功。


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

答案 2 :(得分:3)

它们是允许的,甚至是基于const排名的函数,但由于你不能从const Foo&&引用的const对象移动,它们没用。

答案 3 :(得分:1)

我无法想到这种情况会直接有用,但可以间接使用:

template<class T>
void f(T const &x) {
  cout << "lvalue";
}
template<class T>
void f(T &&x) {
  cout << "rvalue";
}

template<class T>
void g(T &x) {
  f(T());
}

template<class T>
void h(T const &x) {
  g(x);
}

g 中的T是T const,所以 f 的x是T const&amp;&amp;。

这很可能会导致 f 中的漫游错误(当它试图移动或使用该对象时),但 f 可能会采用rvalue-ref,以便如果不修改右值,就不能在左值上调用它(如上面的一个简单例子)。

答案 4 :(得分:1)

除了std::ref之外,标准库还出于相同目的在std::as_const中使用const rvalue引用。

template <class T>
void as_const(const T&&) = delete;

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

constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;

以及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;

大概是为了在访问包装的值时保持值的类别以及包装器的恒定性。

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