移动构造函数未按预期调用

时间:2012-11-07 15:40:02

标签: c++ c++11 move-constructor

我是C ++ 0x的新手,我正试图围绕rvalue引用并移动构造函数。我正在使用带有-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()中的命名对象,但当它们从函数返回时,它们应该成为临时对象。

1 个答案:

答案 0 :(得分:11)

这是你的问题:

return i ? tmp1 : tmp2;

如果return语句只是return var;,函数中的局部变量只会从return语句中移出。如果您想进行该测试,则需要使用if:

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

引用有点复杂,但是在12.8 / 31和12.8 / 32

  

12.8 / 32当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策选择首先执行副本的构造函数,就像对象由rvalue [...]

指定一样

即使表达式是左值,当满足12.8 / 31中的条件时,它将被视为右值,该块中的第二个选项是:

  

12.8 / 31在具有类返回类型的函数的return语句中,当表达式是具有相同cv-unqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时作为函数返回类型,可以通过将自动对象直接构造为函数的返回值来省略复制/移动操作。

确定return tmp;允许复制省略,但return (cond?tmp:tmp);不允许。

请注意,对于编译器在return语句中生成隐式std::move,返回的对象必须是elision的候选对象,除非它也是函数的参数。使用条件操作会禁止复制省略,同时禁止编译器移出对象。第二种情况可能更容易编码:

Foo make_foo(Foo src) {
   return src;           // Copy cannot be elided as 'src' is an argument
}