web-dev-qa-db-ja.com

左辺値から右辺値への参照バインディング

コンパイラーは、左辺値を右辺値参照にバインドしようとしていると不平を言い続けますが、方法がわかりません。 C++ 11の初心者、移動セマンティクスなどですので、ご容赦ください。

私はこの機能を持っています:

template <typename Key, typename Value, typename HashFunction, typename Equals>
Value& FastHash<Key, Value, HashFunction, Equals>::operator[](Key&& key)
{
    //  Some code here...

    Insert(key, Value()); // Compiler error here

    //   More code here.
}

これはこのメソッドを呼び出します:

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(Key&& key, Value&& value)
{
    // ...
}

次のようなエラーが発生し続けます。

cannot convert argument 1 from 'std::string' to 'std::string &&'

insert()呼び出し。演算子オーバーロードでkeyが右辺値として定義されていませんか?なぜそれが左辺値として再解釈されるのですか?

18
Insert(key, Value()); // Compiler error here

keyKey&& keyです-これは左辺値です!それには名前があり、そのアドレスをとることができます。それは、その左辺値の型が「Keyへの右辺値参照」であるということです。

右辺値を渡す必要があり、そのためにはstd::moveを使用する必要があります。

Insert(std::move(key), Value()); // No compiler error any more

これが直感に反する理由がわかります!しかし、右辺値参照(右辺値にバインドされた参照)と実際の右辺値を区別すると、より明確になります。

編集:ここでの本当の問題は、右辺値参照を使用することです。参照の折りたたみ規則により、引数を左辺値参照または右辺値参照のいずれかにバインドできるため、引数の型が推定される関数テンプレートでそれらを使用することは理にかなっています。理由については、この記事とビデオを参照してください: http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

ただし、この場合、FastHash<std::string, ... >をインスタンス化したときにクラスによってすでに決定されているため、関数の呼び出し時にKeyのタイプは推定されません。したがって、実際には右辺値参照の使用を規定しているため、std::moveを使用するとコードが修正されます。

パラメータを値で受け取るようにコードを変更します。

template <typename Key, typename Value, typename HashFunction, typename Equals>
Value& FastHash<Key, Value, HashFunction, Equals>::operator[](Key key)
{
    //  Some code here...

    Insert(std::move(key), Value());

    //   More code here.
}

template <typename Key, typename Value, typename HashFunction, typename Equals>
void FastHash<Key, Value, HashFunction, Equals>::Insert(Key key, Value value)
{
    // ...
}

値の引数を使用することによる余分なコピーについてあまり心配しないでください。これらはコンパイラによって頻繁に最適化されます。

26
polkadotcadaver