14

私は C++0x を初めて使用し、右辺値参照に頭を悩ませ、コンストラクターを移動しようとしています。-std=c++0x で g++ 4.4.6 を使用していますが、次のコードで混乱しています。



    class Foo 
    {
    public:
      Foo() 
        : p( new int(0) )
      {
        printf("default ctor\n");
      }

      Foo( int i )
        : p( new int(i) )
      {
        printf("int ctor\n");
      }

      ~Foo() 
      {
        delete p;
        printf("destructor\n");
      }

      Foo( const Foo& other ) 
        : p( new int( other.value() ) )
      {
        printf("copy ctor\n");
      }


      Foo( Foo&& other )
        : p( other.p )
      {
        printf("move ctor\n");
        other.p = NULL;
      }

      int value() const 
      {
        return *p;
      }

    private:
      // make sure these don't get called by mistake
      Foo& operator=( const Foo& );
      Foo& operator=( Foo&& );

      int* p;
    };


    Foo make_foo(int i) 
    {
      // create two local objects and conditionally return one or the other
      // to prevent RVO
      Foo tmp1(i);
      Foo tmp2(i);

      // With std::move, it does indeed use the move constructor
      //  return i ? std::move(tmp1) : std::move(tmp2);
      return i ? tmp1 : tmp2;

    }


    int main(void) 
    {
      Foo f = make_foo( 3 );

      printf("f.i is %d\n", f.value());

      return 0;
    }

書かれているように、コンパイラーはコピーコンストラクターを使用して main() でオブジェクトを構築することがわかりました。make_foo() 内で std::move 行を使用すると、移動コンストラクターが main() で使用されます。make_foo() 内で std::move が必要なのはなぜですか? tmp1 と tmp2 は make_foo() 内の名前付きオブジェクトですが、関数から返されると一時的になるはずだと思います。

4

1 に答える 1

11

これはあなたの問題です:

return i ? tmp1 : tmp2;

関数内のローカル変数は、return ステートメントがただの場合にのみ return ステートメントから移動されreturn var;ます。そのテストを行いたい場合は、if を使用する必要があります。

if (i) {
   return tmp1;
} else {
   return tmp2;
}

引用は少し複雑ですが、12.8/31 と 12.8/32 にあります。

12.8/32 コピー操作の省略の基準が満たされるか、またはソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コンストラクターを選択するためのオーバーロード解決オブジェクトが右辺値によって指定されたかのように、最初にコピーが実行されます [...]

つまり、式が左辺値であっても、12.8/31 の基準が満たされると右辺値と見なされます。そのブロックの 2 番目のオプションは次のとおりです。

12.8/31 クラスの戻り値の型を持つ関数の return ステートメントで、式が関数と同じ cv-unqualified 型を持つ非揮発性自動オブジェクト (関数または catch 節パラメーター以外) の名前である場合戻り値の型、自動オブジェクトを関数の戻り値に直接構築することにより、コピー/移動操作を省略できます。

どちらreturn tmp;がコピーの省略を許可するかを決定しますが、許可しreturn (cond?tmp:tmp);ません。

コンパイラがstd::movereturn ステートメントで暗黙的を生成するには、返されたオブジェクトが関数の引数でもある場合を除き、返されたオブジェクトが省略の候補である必要があることに注意してください。条件付き操作を使用すると、コピーの省略が禁止され、同時に、コンパイラがオブジェクトから移動するのが禁止されます。その 2 番目のケースは、コーディングが簡単な場合があります。

Foo make_foo(Foo src) {
   return src;           // Copy cannot be elided as 'src' is an argument
}
于 2012-11-07T16:02:18.293 に答える