14

重複の可能性:
C ++11でのT&&の意味は何ですか?

どういうわけか、これは私の直感を逃しており、インターネット上で説明を見つけることができません。C ++関数が参照の参照を取得することはどういう意味ですか?例えば:

void myFunction(int&& val);     //what does this mean?!

参照渡しの考え方を理解しているので、

void addTwo(int& a)
{
    a += 2;
}

int main()
{
    int x = 5;
    addTwo(x);

    return 0;
}

動作し、私には直感的です。

4

2 に答える 2

37

これは参照の参照ではなく、プログラムの他の場所で参照されておらず、破壊的に変更できるメモリ内のオブジェクトへの参照を(非公式に)表す右辺値参照と呼ばれる新しい言語機能です。たとえば、関数の戻り値は、式に導入された一時的な値と同様に、右辺値参照によって取得できます。

右辺値参照は、さまざまな目的に使用できます。ほとんどのC++プログラマーの観点からは、これらを使用して移動セマンティクスを実装できます。これにより、古いオブジェクトの内容を古いオブジェクトから新しいオブジェクトに「移動」することで、新しいオブジェクトを初期化できます。これを使用すると、オブジェクトをコピーするために莫大な費用をかけることなく、C ++ 11の関数から巨大なオブジェクトを返すことができます。これは、戻り値のキャプチャに使用されるオブジェクトが、一時オブジェクトから内部を盗むだけでmoveコンストラクターを使用して初期化できるためです。 returnステートメントによって作成されます。

移動セマンティクスはコピーセマンティクスと直交しているため、オブジェクトはコピー可能でなくても移動可能です。たとえば、std::ofstreamsはコピーできませんが、移動可能であるためstd::ofstream、移動動作を使用して関数からsを返すことができます。現在、これはC++03では実行できません。たとえば、このコードはC ++ 03では違法ですが、C ++ 11では完全に問題ありません(推奨されています)。

std::ifstream GetUserFile() {
    while (true) {
        std::cout << "Enter filename: ";
        std::string filename;
        std::getline(std::cin, filename);

        ifstream input(filename); // Note: No .c_str() either!
        if (input) return input;

        std::cout << "Sorry, I couldn't open that file." << std::endl;
    }
}

std::ifstream file = GetUserFile(); // Okay, move stream out of the function.

直感的には、右辺値参照を受け取る関数は、(おそらく)古いオブジェクトの内容を新しいオブジェクトに移動することによって高価なコピーを回避しようとしている関数です。たとえば、ベクトルのようなオブジェクトの移動コンストラクターを定義するには、そのコンストラクターに右辺値参照を取り込むことができます。ベクトルを配列へのポインター、配列の容量、および使用済みスペースのトリプルとして表す場合、次のようにその移動コンストラクターを実装できます。

vector::vector(vector&& rhs) {
    /* Steal resources from rhs. */
    elems    = rhs.elems;
    size     = rhs.size;
    capacity = rhs.capacity;

    /* Destructively modify rhs to avoid having two objects sharing 
     * an underlying array.
     */
    rhs.elems    = nullptr; // Note use of nullptr instead of NULL
    rhs.size     = 0;
    rhs.capacity = 0;
}

rhsコンストラクターの最後でクリアすると、最終的に次のrhsような状態になることに注意することが重要です。

  1. デストラクタが呼び出したときにクラッシュを引き起こしません(nullptr解放nullptrは安全なので、要素ポインタをに設定していることに注意してください)。
  2. それでも、オブジェクトに新しい値を割り当てることができます。この後者の点は注意が必要ですが、ある時点で、クリアされたオブジェクトに新しい値を与えることができるようにすることが重要です。これは、プログラムの後半で引き続き参照できるオブジェクトへの右辺値参照を取得できるためです。

(2)に光を当てるために、右辺値参照の興味深いユースケースの1つは、オブジェクト間で値を明示的に移動する機能です。たとえば、次の慣用的な実装について考えてみますswap

template <typename T> void swap(T& lhs, T& rhs) {
    T temp = lhs;
    lhs = rhs;
    rhs = temp;
}

このコードは合法ですが、少し珍しいものです。特に、3つのコピーを作成することになります。最初tempはのコピーに等しい設定lhs、1つはのコピーに設定、もう1つlhsはのコピーにrhs設定rhsする場合ですtemp。しかし、ここではコピーを作成したくありません。代わりに、値をシャッフルしたいだけです。std::moveしたがって、C ++ 11では、次の関数を使用して、オブジェクトへの右辺値参照を明示的に取得できます。

template <typename T> void swap(T& lhs, T& rhs) {
    T temp = std::move(lhs);
    lhs = std::move(rhs);
    rhs = std::move(temp);
}

現在、コピーはまったく作成されていません。の内容をに移動lhstemp、次にの内容をに移動rhslhs、次にの内容をに移動temprhsます。そうすることで、新しい値を入れる前に、一時的に両方lhsと「空」の状態のままにしました。rhsオブジェクトからコンテンツを移動するコードを作成するときは、このコードが正しく機能するように、オブジェクトをある程度整形式の状態のままにしておくことが重要です。

于 2011-08-22T21:48:22.910 に答える
2

参照への参照ではありません。これは、いわゆる右辺値参照用にC++0xで導入された新しい構文です。

于 2011-08-22T21:48:56.893 に答える