web-dev-qa-db-ja.com

RValue参照(&&)の戻りが役立つ場合はありますか?

関数がRValue参照を返す必要がある理由はありますか?テクニック、トリック、またはイディオムまたはパターン?

_MyClass&& func( ... );
_

私は 参照を返す の危険性を一般的に認識していますが、とにかくそうすることもあります(T& T::operator=(T)は慣用的な例の1つにすぎません)。しかし、T&& func(...)はどうですか?そうすることで得られる一般的な場所はありますか?おそらく、クライアントコードだけと比較して、ライブラリまたはAPIコードを書くときは異なりますか?

73
towi

適切な場合もいくつかありますが、それらは比較的まれです。 1つの例では、クライアントがデータメンバーから移動できるようにする必要がある場合が考えられます。例えば:

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};
57
Howard Hinnant

これは、towiのコメントに続きます。ローカル変数への参照を返す必要はありません。しかし、あなたはこれを持っているかもしれません:

vector<N> operator+(const vector<N>& x1, const vector<N>& x2) { vector<N> x3 = x1; x3 += x2; return x3; }
vector<N>&& operator+(const vector<N>& x1, vector<N>&& x2)    { x2 += x1; return std::move(x2); }
vector<N>&& operator+(vector<N>&& x1, const vector<N>& x2)    { x1 += x2; return std::move(x1); }
vector<N>&& operator+(vector<N>&& x1, vector<N>&& x2)         { x1 += x2; return std::move(x1); }

これにより、両方のパラメーターが左辺値である場合を除いて、すべてのケースでコピー(および可能な割り当て)が防止されます。

17
Clinton

いいえ。値を返すだけです。一般に参照を返すことはまったく危険ではありません-危険なlocal変数への参照を返すことです。ただし、右辺値参照を返すことは、ほとんどすべての状況でかなり価値がありません(あなたがstd::move か何か)。

7
Puppy

関数の終了後、参照されたオブジェクトがスコープ外にならないことが確実な場合は、参照によって戻ることができます。これは、グローバルオブジェクトの参照、またはクラスフィールドへの参照を返すメンバー関数などです。

この返される参照ルールは、左辺値と右辺値の両方の参照と同じです。違いは、返された参照をどのように使用するかです。私が見ることができるように、右辺値参照によって戻ることはまれです。機能がある場合:

Type&& func();

あなたはそのようなコードを気に入らないでしょう:

Type&& ref_a = func();

名前付き右辺値参照は左辺値であるため、ref_aをType&として効果的に定義し、実際の移動はここでは実行されないためです。それは非常に似ています:

const Type& ref_a = func();

ただし、実際のref_aは非定数の左辺値参照です。

また、Type &&引数を取る別の関数にfunc()を直接渡しても、それはまだその関数内の名前付き参照であるため、あまり役に立ちません。

void anotherFunc(Type&& t) {
  // t is a named reference
}
anotherFunc(func());

Func()とanotherFunc()の関係は、func()がanotherFunc()がfunc()から返されたオブジェクトの所有権を取得する(または「盗む」と言うことができる)ことに同意する「承認」に似ています。しかし、この合意は非常に緩いです。非const左辺値参照は、呼び出し側によって依然として「盗まれる」可能性があります。実際、関数が右辺値参照引数を取るように定義されることはほとんどありません。最も一般的なケースは、「anotherFunc」がクラス名であり、anotherFunc()が実際には移動コンストラクターである場合です。

0
hangyuan

もう1つの可能なケース:タプルをアンパックし、値を関数に渡す必要がある場合。

この場合、コピー削除が不明な場合に役立ちます。

そのような例:

template<typename ... Args>
class store_args{
    public:
        std::Tuple<Args...> args;

        template<typename Functor, size_t ... Indices>
        decltype(auto) apply_helper(Functor &&f, std::integer_sequence<size_t, Indices...>&&){
            return std::move(f(std::forward<Args>(std::get<Indices>(args))...));
        }

        template<typename Functor>
        auto apply(Functor &&f){
            return apply_helper(std::move(f), std::make_index_sequence<sizeof...(Args)>{});
        }
};

ただし、何らかの形でstd::bindまたはstd::threadの置き換えを記述している場合を除き、かなりまれなケースです。

0
CoffeeandCode