对const的右值引用有什么用吗?


Answers:


78

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

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

上面的两个重载确保另一个ref(T&)cref(const T&)函数不绑定到右值(否则可能会发生)。

更新资料

我刚刚检查了官方标准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;

查看cppreference似乎不再是这种情况了。有什么想法吗?还有其他地方const T&&吗?
Pubby 2012年

58
为什么您在答案中包含三次相同的代码?我试图找到一个差异太久了。
typ1232

9
@ typ1232:似乎我在回答了将近两年后更新了答案,原因是评论中担心不再引用引用的功能。我从N3290和当时最新的N3485草稿中进行了复制/粘贴,以表明功能仍然存在。在我当时的想法中,使用复制/粘贴是确保比我更多的眼睛可以确认我没有忽略这些签名中一些细微变化的最佳方法。
Howard Hinnant 2013年

1
@kevinarpe:“其他重载”(此处未显示,但在标准中)采用左值引用,并且不会被删除。此处显示的重载比使用左值引用的重载更适合右值。因此,右值参数绑定到此处显示的重载,然后会导致编译时错误,因为这些重载已删除。
霍华德·辛南特

1
使用const T&&prevent可以防止某些人愚蠢地使用形式为的显式模板args ref<const A&>(...)。这不是一个非常强烈的论点,但是const T&&over的代价T&&却很小。
Howard Hinnant

6

获得常量值引用(而不是=delete)的语义是说:

  • 我们不支持左值运算!
  • 即使我们仍然复制,因为我们无法移动传递的资源,或者因为“移动”它没有实际意义。

以下用例可能是恕我直言,非常适合对const进行右值引用,尽管该语言决定不采用这种方法(请参见原始SO post)。


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

通常建议使用make_uniquemake_shared,但两者都可以,unique_ptr并且shared_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能防止上述,如果他们的相关构造期望得到原始指针作为一个const右值,例如用于unique_ptr

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

在这种情况下,上面的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

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


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

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

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

如果我们只希望使用rvalue引用,则不会执行此操作(编译错误:无法将const rvalue绑定到rvalue)。


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


我是有类似想法的人,但我没有前进的支持。
Red.Wave

“自动p = std :: unique_ptr {std :: move(ptr)};” 不会出现错误“类模板参数推导失败”的错误。我认为应该是“ unique_ptr <int>”。
林则辉

4

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


您所说的“排名”是什么意思?我猜这与过载解析有关吗?
fredoverflow

如果给定的类型具有采用const rvalue-ref的move ctor,为什么不从const rvalue-ref移走呢?
弗雷德·纽克

6
@FredOverflow,过载的排名是这样的:const T&, T&, const T&&, T&&
Gene Bushuyev 2011年

2
@Fred:如何在不修改源代码的情况下移动?
fredoverflow

3
@Fred:可变数据成员,或者移动到此假设类型不需要修改数据成员。
Fred Nurk

2

除了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限定函数的任何用途。


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);
}

的T IN为T const的,所以˚F的x是一个常量Ť&&。

可能会导致f发生编译错误(当它尝试移动或使用对象时),但是f可以采用rvalue-ref,这样就不能在不修改rvalue的情况下在lvalues上调用它(如太简单了)以上示例)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.