Подтвердить что ты не робот

Перемещение конструктора не называется ожидаемым

Я новичок в С++ 0x, и я пытаюсь обернуть голову вокруг ссылок rvalue и переместить конструкторы. Я использую g++ 4.4.6 с -std = С++ 0x, и меня путает следующий фрагмент кода:



    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(). Когда я использую строку std:: move внутри make_foo(), конструктор move используется в main(). Почему std:: move необходим внутри make_foo()? Я бы подумал, что хотя tmp1 и tmp2 называются объектами внутри make_foo(), когда они возвращаются из функции, они должны стать временными.

4b9b3361

Ответ 1

Это ваша проблема:

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 Когда критерии для выполнения операции копирования выполняются или выполняются, за исключением того факта, что исходный объект является параметром функции, а подлежащий копированию объект определяется значением lvalue, разрешением перегрузки для выбора конструктор для копии сначала выполняется так, как если бы объект был обозначен rvalue [...]

Это даже если выражение является lvalue, оно будет считаться значением r, когда удовлетворяются критерии в 12.8/31, вторая опция в этом блоке:

12.8/31 в операторе return в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (отличного от функции или параметра catch-clause) с тем же cv-неквалифицированным типом в качестве возвращаемого типа функции операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции.

Определяет, что 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
}