2015-11-02 3 views
5

Предположим, у меня есть:В чем разница между авто a = A (3) и A a (3)?

struct A 
{ 
    A(int x) : m_x(x) { } 
    A(A&&) = delete; 
    int m_x; 
} 

и:

A a(3); // Ok 
auto a = A(3); // Error: function A(int&&) cannot be referenced - it's a deleted function 

Почему последний вызов конструктора двигаться? Почему эти 2 выражения отличаются с точки зрения сгенерированного кода?

ответ

4

Почему вы ожидали, что оба пути кода будут одинаковыми?

Вы явно построив неназванный объект во втором примере, выделяя a (auto a), а затем скопировать из временной в a (это движение, потому что мы говорим о временном объекте).

+0

Что касается вашего первого комментария, у меня сложилось впечатление, что это часто вводится как «все те же, за исключением технических проблем, которые обычно не возникают». Но, возможно, я помещаю слова в рот. – Hurkyl

+1

Тот, кто вводит их как таковые, должен быть уволен за некомпетентность. – Blindy

+0

Почему компилятор генерирует другой код, когда намерение явно одно и то же? Есть ли случай, когда программист захочет этого поведения? – Shmoopy

-1

Не имеет значения, что компилятор будет генерировать один и тот же код в обоих случаях, потому что во время компиляции необходимы необходимые функции/конструкторы. Подумайте об этом так: оптимизация произойдет после компиляции/разбора кода, но в конечном итоге код будет (должен быть) таким же в этом случае.

+0

код будет скомпилирован в один и тот же машинный код * с оптимизацией на * – CoffeeandCode

6

auto a = A(3); означает, что A a = A(3); так как тип правой стороны A.

Это означает именно то, что он выглядит следующим образом: A(3) создает временный A инициализированную 3, а затем A a = _____ означает: создать A называется a с _____ как инициализаторе.

Таким образом, вы создаете временное, передаете это значение a в качестве инициализатора, а затем временное уничтожается. Этот тип инициализации (с =) называется копия-инициализация (хотя не путайте это с «копией», это всего лишь слово).

Конструктор выбирается для построения a, который принимает A. Это должен быть либо экземпляр, либо конструктор перемещения. A имеет конструктор перемещения и конструктор копирования. Последний неявно генерируется и определяется как удаленный, так как существует объявленный пользователем конструктор перемещения.

Определенный как удаленный, не влияет на разрешение перегрузки; и в этом случае конструктор move предпочтительнее конструктора копирования.

Таким образом, ваш код пытается вызвать функцию delete d, которая является плохо сформированной, следовательно, является ошибкой.

Обратите внимание, что если конструктор перемещения не был удален, то применяется копия elision. Он запускается в некоторых случаях, когда переменная инициализируется из временной или локальная переменная возвращается значением. Правило состоит в том, что компилятор может использовать одну и ту же память как для a, так и для временного объекта и опустить вызов конструктору copy/move.

Большинство/все составители действительно сделают это в этом случае. Поэтому вы можете написать auto a = A(3); и на практике не получить лишних ходов. Если вы напишете какой-то код для вашего конструктора move, который выводит что-то, вы, надеюсь, обнаружите, что ничего не выводится.

Если вы хотите абсолютно убедиться в отсутствии ненужной копии или создать объект, который не имеет пригодной для использования копии, и не перемещать конструктор - прекратите писать код, который указывает ненужные копии! A a(3); достаточный.

+0

Интересно, удастся ли компилятору преодолеть перемещение в этом случае, когда конструктор перемещения удален? – Mikhail

+0

@ Михаэль извините, неверный вопрос. обновит мой ответ –

Смежные вопросы