7

右辺値参照の簡単な紹介では、非 const 参照引数を持つコンストラクターに右辺値 5 を転送するための理想的なソリューションとして、完全な転送が提案されています。

しかし:

#include <memory>
#include <iostream>
#include <utility>

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
}

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }
};


int main() {
    std::shared_ptr<X> p = factory<X>(5);
}

XCode 4.2 ans G++ 4.6.1 では失敗しますが、次のようになりno known conversion from int to int&ます。

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}

コンパイルします。何を間違えたのですか?

4

3 に答える 3

5

完全な転送は、非定数参照引数を持つコンストラクターに右辺値5を転送するための理想的なソリューションとして提案されています。

完璧な転送がそれを意味するとは思いません。記事が正しければ、それを示唆することはできません。

むしろ、右辺値参照を右辺値として転送できることを意味するため、move-constructorまたは右辺値参照を受け取るコンストラクター/関数を呼び出します。

だからあなたはこれを試してみるべきです:

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }

    //this is a constructor which takes rvalue references
    X(int&& i){ 
        std::cout<<"X("<<i<<")\n";
    }
};

つまり、からfactory、作成したコンストラクターではなく、2番目のコンストラクターを呼び出す必要があります。

ちなみに、この場合、パラメーター型intは基本型なので、コンストラクターはあまり意味がありません。

パラメータタイプとしての右辺値参照は、リソースを管理するクラスのmove-constructorおよびmove-assignmentを定義するために使用されます。ユーザー定義クラスがリソースを管理しない場合、move-semanticsは意味がありません。

于 2011-12-02T11:08:47.493 に答える
5

右辺値を非 const 左辺値参照にバインドすることはできません。この記事では、そのために完全転送を使用することはお勧めしません。それは不可能だからです。完全転送は、左辺値を左辺値として、右辺値を右辺値として転送します。

ここで、 forward は factory に渡された引数の左辺値/右辺値性を保持します。右辺値が factory に渡される場合、右辺値は forward 関数の助けを借りて T のコンストラクターに渡されます。同様に、左辺値が factory に渡されると、左辺値として T のコンストラクターに転送されます。

この例のコンストラクターは左辺値のみを受け取るため、左辺値のみをファクトリ関数に渡すことができます。右辺値を渡すと、右辺値として転送されますが、そのコンストラクターに右辺値を渡す方法がないため、これは不適切な形式になります。

于 2011-12-02T11:10:58.070 に答える
3

右辺値参照を一瞬無視し、代わりにこれが許可されているふりをします。

void modify_int(int& i)
{
    i = 1;
}

void foo(int& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(i); // makes i = 1

// illegal in standard C++, cannot bind a temporary to a non-const reference
foo(5); // makes the temporary integer equal to 1

一時オブジェクトが変更されていることがわかりますが、これはまったく問題ありません。ただし、通常は望ましくないため、このバインドは C++ では違法になりました (結局、5 が 1 に変更されたかのように読み取られます)。

右辺値参照が行うことはすべて、一時的な値を参照にバインドできるようにすることですが、一時的な値と見なすべき値を扱っていることを理解しているため安全です。

void modify_int(int& i)
{
    i = 1;
}

void foo(int&& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(std::move(i)); // makes i = 1 (std::move makes it an rvalue)

// legal in C++11, temporary is bound to rvalue-reference
foo(5); // makes the temporary integer equal to 1

このバージョンのfooでは、 に渡すことmodify_intはまったく問題ないことに注意してください。関数内に入ると、それが左辺値参照ではなく右辺値参照であったという事実は関係ありません。参照するオブジェクトがまだあります。値カテゴリを保持するために、テンプレートで転送が使用されます

void test(int& i) {} // lvalue version of test
void test(int&& i) {} // rvalue version of test

template <typename T>
void foo(T&& x)
{
    // if x was an lvalue, forward does nothing;
    // if x was an rvalue, forward std::move's it 
    test(std::forward<T>(x)); 
}

int i = 7;
foo(i); // calls lvalue version of test

foo(5); // calls rvalue version of test

転送なしのコードは、私の回答の 2 番目のスニペットに似ています。factory関数内に入るa1と、通常の左辺値であり、コンストラクタ参照にバインドされます。しかし、転送すると、(右辺値でfactory(5)呼び出すため) 右辺値に戻り、左辺値参照にバインドできず、エラーが発生します。

于 2011-12-02T11:18:18.503 に答える