web-dev-qa-db-ja.com

constへの右辺値参照には用途がありますか?

私はそうは思いませんが、確認したいと思います。 Fooがクラス型であるconst Foo&&の使用はありますか?

76
fredoverflow

それらは時々役立ちます。ドラフト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;
_
67
Howard Hinnant

これらは許可され、関数はconstに基づいてランク付けされますが、const Foo&&によって参照されるconstオブジェクトから移動できないため、それらは役に立ちません。

4
Gene Bushuyev

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修飾関数の用途はわかりません。

1
eerorika

これが直接役立つ状況は考えられませんが、間接的に使用される可能性があります。

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は右辺値参照を取得できるため、 (上記の非常に単純な例のように)右辺値を変更せずに左辺値を呼び出します。

1
Fred Nurk

const rvalue reference(および=deleteではない)を取得するセマンティクスは、

  • 左辺値の操作はサポートされていません!
  • たとえまだコピーします(渡されたリソースをmoveできないため、または「移動」するための実際の意味がないため) 。

次のユースケースである可能性がありますIMHO rvalue reference to constの良いユースケースですが、言語はこのアプローチを採用しないことを決定しました( 元のSO投稿 )。


ケース:生のポインターからのスマートポインターコンストラクター

通常、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は、関連するコンストラクターが生のポインターを取得することを期待している場合、上記を防ぐことができます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_ptrfrom const pointerの作成を許可するためです。 const rvalue referenceは、constnon-constの両方を受け入れるため、単なるrvalue referenceよりも一般的です。したがって、以下を許可できます。

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

rvalue reference(コンパイルエラー:const rvalueにバインドできないので、これはうまくいきません。/rvalue)。


とにかく、これはそんなことを提案するには遅すぎる。しかし、この考えはconstへの参照値の合理的な使用法を示しています。

0
Amir Kirsh