Конструктор перемещения не вызывается должным образом - PullRequest
0 голосов
/ 17 января 2019

Мой пользовательский класс Integer ниже:

class Integer
{
private:
    int * ptr_int_; // Resource
public:
    // Other ctors
    Integer(Integer &&); // Move constructor
    // dtor
}

Конструктор перемещения реализован следующим образом:

Integer::Integer(Integer && arg)
{
    std::cout << "Integer(Integer && arg) called\n";
    this->ptr_int_ = arg.ptr_int_; // Shallow Copy
    arg.ptr_int_ = nullptr;
}

В моем драйвере, для звонка ниже,

Integer obj2{ Integer{5}}; 

Я ожидал, что будет вызван параметризованный конструктор (для временного объекта), а затем перемещен конструктор. Однако конструктор перемещения не был вызван.

При разборке я получил материал, показанный ниже:

Integer obj2{ Integer{5}}; 
001E1B04  push        4  
001E1B06  lea         ecx,[obj2]  
001E1B09  call        Integer::__autoclassinit2 (01E1320h)  
001E1B0E  mov         dword ptr [ebp-114h],5  
001E1B18  lea         eax,[ebp-114h]  
001E1B1E  push        eax  
001E1B1F  lea         ecx,[obj2]  ;; Is this copy elision(RVO) in action?
001E1B22  call        Integer::Integer (01E12FDh)  
001E1B27  mov         byte ptr [ebp-4],1

Полагаю, это Оптимизация Возвращаемого значения (RVO) в действии. Я прав?

Поскольку большинство компиляторов реализуют RVO, я не должен делать

 Integer obj2{ std::move(Integer{5})}; 

Должен ли я?

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Конструктор перемещения не вызывается из-за RVO, как вы сами выяснили.

Обратите внимание, что std::move просто переводит свой аргумент в ссылку на rvalue. В вашем примере Integer{5} является неназванным временным и уже является значением. Поэтому дополнительный вызов std::move не нужен. Конструктор перемещения будет вызван в любом случае, если он не полностью исключен, как в вашем случае.

Также обратите внимание, что в вашей реализации конструктора перемещения дополнительная std::move не требуется, поскольку ptr_int_ является необработанным указателем без какой-либо специальной семантики перемещения.

0 голосов
/ 17 января 2019

Это сложный вопрос, потому что он технически изменился в c ++ 17. В c ++ 11 это оптимизация NRVO, но в c ++ 17 это даже не оптимизация.

  1. Вы не должны ожидать перемещения, это зависит от компилятора.
  2. Поскольку c ++ 17 вы не можете этого ожидать, его нельзя вызвать.

Соответствующий отрывок из cppreference :

При следующих обстоятельствах компиляторы должны опускать конструкцию копирования и перемещения объектов класса, даже если конструктор копирования / перемещения и деструктор имеют наблюдаемые побочные эффекты. Объекты создаются непосредственно в хранилище, где они в противном случае были бы скопированы / перемещены. Конструкторы копирования / перемещения не обязательно должны присутствовать или быть доступными , поскольку языковые правила гарантируют, что операция копирования / перемещения не выполняется, даже концептуально :

  • [...]

  • При инициализации переменной, когда выражение инициализатора является prvalue того же типа класса (игнорируя квалификацию cv), что и тип переменной:

T x = T(T(f())); // only one call to default constructor of T, to initialize x

Примечание: вышеприведенное правило не определяет оптимизацию: спецификация базовых языков C ++ 17 для значений и временных характеристик принципиально отличается от таковой в более ранних ревизиях C ++: больше нет временного объекта для копирования / перемещения. Еще один способ описать механику C ++ 17 - это «нематериализованная передача значений»: prvalues ​​возвращаются и используются без материализации временного.

Акцент мой на важной части. Вышеупомянутый абзац существует с c ++ 17 и не существует в c ++ 11.

Теперь c ++ 11:

При следующих обстоятельствах компиляторам разрешено, но не обязательно пропускать конструкцию объектов класса copy и move (начиная с C ++ 11), даже если конструктор copy / move (начиная с C ++ 11) и деструктор имеют наблюдаемые побочные эффекты. Объекты создаются непосредственно в хранилище, где они в противном случае были бы скопированы / перемещены. Это оптимизация: даже когда это происходит и конструктор копирования / перемещения (начиная с C ++ 11) не вызывается, он все равно должен быть и доступен (как если бы оптимизация не происходила в все), иначе программа некорректна :

  • [...]

  • При инициализации объекта, когда исходный объект является временным безымянным и имеет тот же тип класса (игнорирующий квалификацию cv), что и целевой объект. Когда безымянный временный оператор является операндом оператора возврата, этот вариант разрешения копирования известен как RVO, «оптимизация возвращаемого значения». (до с ++ 17)

Это ваш случай. Так что для c ++ 11 это RVO для инициализации. return заявление, что RVO фактически охвачено другой пулей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...