我猜不是,但我想确认一下。有什么用const Foo&&
,Foo
类类型在哪里?
5 回答
它们有时很有用。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 [function.objects]/p2 中它仍然说:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
获取const 右值引用(而不是 for )的语义=delete
是为了说:
- 我们不支持左值操作!
- 即使如此,我们仍然 copy,因为我们无法移动传递的资源,或者因为“移动”它没有实际意义。
恕我直言,以下用例可能是对 const进行右值引用的一个很好的用例,尽管该语言决定不采用这种方法(请参阅原始 SO 帖子)。
案例:来自原始指针的智能指针构造函数
通常建议使用make_unique
and make_shared
,但两者unique_ptr
andshared_ptr
都可以从原始指针构造。两个构造函数都通过值获取指针并复制它。两者都允许(即:不阻止)在构造函数中继续使用传递给它们的原始指针。
以下代码使用double free编译和生成:
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
如果它们的相关构造函数期望将原始指针作为 const rvalue获取,则两者都unique_ptr
可以防止上述情况,例如:shared_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
这样的 bug 就会落入共同的规则:不要使用被移动的资源。
有人可以问:好的,但是为什么呢?T* const&& p
原因很简单,允许unique_ptr
从 const 指针创建。请记住,const 右值引用比右值引用更通用,因为它同时接受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 引用的对象中移动const Foo&&
,因此它们没有用。
除了std::ref,标准库还在std::as_const中使用 const 右值引用来实现相同的目的。
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可以采用 rvalue-ref 以便在不修改右值的情况下无法在左值上调用它(如在太简单的上面的例子)。