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() 中使用了 move 构造函数。为什么在 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 中的条件时,它也会被视为右值,该块中的第二个选项是:

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

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

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

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