私はそうは思いませんが、確認したいと思います。 Foo
がクラス型であるconst Foo&&
の使用はありますか?
それらは時々役立ちます。ドラフトC++ 0x自体は、いくつかの場所でそれらを使用します。次に例を示します。
_template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
_
上記の2つのオーバーロードは、他のref(T&)
およびcref(const T&)
関数が右辺値にバインドしないことを保証します(そうでなければ可能です)。
更新
公式標準 N329 を確認したところ、残念ながら一般には入手できません。また、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;
_
これらは許可され、関数はconst
に基づいてランク付けされますが、const Foo&&
によって参照されるconstオブジェクトから移動できないため、それらは役に立ちません。
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修飾関数を呼び出すことができるかどうかが異なります。とは言っても、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(オブジェクトを移動または使用しようとしたとき)でコミールエラーが発生する可能性がありますが、-fは右辺値参照を取得できるため、 (上記の非常に単純な例のように)右辺値を変更せずに左辺値を呼び出します。
const rvalue reference(および=delete
ではない)を取得するセマンティクスは、
次のユースケースである可能性がありますIMHO rvalue reference to constの良いユースケースですが、言語はこのアプローチを採用しないことを決定しました( 元のSO投稿 )。
通常、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
を呼び出す必要がある場合、このようなバグは、移動したリソースを使用しないという一般的なルールに該当します。
質問できます:OK、でもなぜT*
const&& p
?
理由は単純で、unique_ptr
from const pointerの作成を許可するためです。 const rvalue referenceは、const
とnon-const
の両方を受け入れるため、単なるrvalue referenceよりも一般的です。したがって、以下を許可できます。
int* const ptr = new int(9);
auto p = std::unique_ptr { std::move(ptr) };
rvalue reference(コンパイルエラー:const rvalueをにバインドできないので、これはうまくいきません。/rvalue)。
とにかく、これはそんなことを提案するには遅すぎる。しかし、この考えはconstへの参照値の合理的な使用法を示しています。