【发布时间】:2012-11-30 19:32:32
【问题描述】:
我猜不是,但我想确认一下。 const Foo&& 有什么用,其中Foo 是类类型?
【问题讨论】:
-
在这个视频中,STL 说
const&&非常重要,尽管他没有说明原因:youtube.com/watch?v=JhgWFYfdIho#t=54m20s
标签: c++ c++11 constants rvalue-reference
我猜不是,但我想确认一下。 const Foo&& 有什么用,其中Foo 是类类型?
【问题讨论】:
const&& 非常重要,尽管他没有说明原因:youtube.com/watch?v=JhgWFYfdIho#t=54m20s
标签: c++ c++11 constants rvalue-reference
它们有时很有用。 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;
【讨论】:
diff 验证第一个和最后一个文件是否相等,第二个不同。
const T&& 的使用防止有人愚蠢地使用 ref<const A&>(...) 形式的显式模板参数。这不是一个非常有力的论据,但const T&& 相对于T&& 的成本非常低。
获取const rvalue reference(而不是=delete)的语义是为了说明:
以下用例可能是恕我直言,rvalue reference to const 的一个很好的用例,尽管该语言决定不采用这种方法(请参阅original SO post) .
通常建议使用make_unique 和make_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_ptr 和 shared_ptr 如果它们的相关构造函数期望将原始指针 作为 const rvalue 获取,例如对于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* <b><i>const</i></b>&& p?
原因很简单,允许创建unique_ptr从常量指针。请记住,const rvalue reference 比 rvalue reference 更通用,因为它同时接受 const 和 non-const。所以我们可以允许:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
如果我们只期望 rvalue 引用,这将不会发生(编译错误:无法将 const rvalue 绑定到 rvalue)。
无论如何,现在提出这样的事情已经太迟了。但是这个想法确实提出了一个合理使用 对 const 的右值引用。
【讨论】:
它们是允许的,甚至函数根据const 进行排名,但由于您无法从const Foo&& 引用的 const 对象中移动,它们没有用处。
【讨论】:
const T&, T&, const T&&, T&&
除了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-qualified 函数产生影响。也就是说,我不知道 const rvalue ref 限定函数的任何用途。
【讨论】:
我想不出这会直接有用的情况,但它可能会被间接使用:
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&&。
这很可能会导致 f 中的 comile 错误(当它试图移动或使用对象时),但 f 可以采用右值引用,这样不能在不修改右值的情况下在左值上调用它(如上面过于简单的示例)。
【讨论】: