Почему конструктор копирования НЕ вызывается для копирования временного объекта в новый определенный объект

#include <iostream>
using namespace std;

class Y {
public:
    Y(int ) {
        cout << "Y(int)\n";
    }
    Y(const Y&) {
        cout << " Y(const Y&)\n";
    }
};

int main() {
    Y obj1 = 2; // Line 1
}

Выход: Y (целое число)

Ожидаемый результат: Y(int) Y(const Y&)

Вопрос> Насколько я понимаю, Строка 1 сначала создаст временный объект Y(2), а затем назначит временный объект obj1. Таким образом, я ожидаю, что будут вызваны как Y(int), так и Y(const Y&). Но вывод vs2010 сообщает только о первом (т.е. Y(int)). Почему?


person q0987    schedule 25.05.2013    source источник


Ответы (3)


Почему?

Потому что при определенных условиях (указанных в параграфе 12.8/31 стандарта С++ 11) вызовы конструктора копирования или конструктора перемещения могут быть исключены, даже если эти специальные функции (или деструктор) имеют побочные эффекты:

Это исключение операций копирования/перемещения, называемое исключением копирования, разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):

— [...]

- когда временный объект класса, который не был привязан к ссылке (12.2), будет скопирован/перемещен в объект класса с тем же типом cv-unqualified, операцию копирования/перемещения можно опустить, создав временный объект непосредственно в цель пропущенной копии/перемещения

— [...]

Это единственное исключение из так называемого правила "как если бы", которое обычно ограничивает виды преобразований (оптимизаций), которые компилятор может выполнять в программе, чтобы сохранить ее наблюдаемое поведение.

Обратите внимание, что описанный выше механизм называется copy elision, даже если на самом деле это вызов конструктора перемещения, который игнорируется.

person Andy Prowl    schedule 25.05.2013

Это связано с тем, что конструктор имеет только один параметр, и он не помечен explicit, поэтому компилятор автоматически выполняет:

Y obj1 = 2;

В:

Y obj1(2);

Чтобы предотвратить такое поведение, используйте:

explicit Y(int ) {
    cout << "Y(int)\n";
}

(и компиляция в вашем случае не удастся).

person Stefano Sanfilippo    schedule 25.05.2013
comment
Тот факт, что explicit вызывает ошибку компиляции, означает, что = 2 не превращается волшебным образом в (2). Все, что делает explicit, — это предотвращает неявные преобразования в ваш тип. - person chris; 25.05.2013
comment
чтобы предотвратить такое поведение [...], и в вашем случае компиляция завершится ошибкой. - person Stefano Sanfilippo; 25.05.2013

Это называется copy initialization. Y(int ) — это конструктор преобразования. то есть

конструктор с одним параметром, объявленный без явного спецификатора функции

компилятору разрешено опустить лишнюю копию и использовать ваш conversion constructor. Что значит

Y obj1 = 2; // Line 1

эквивалентно

Y obj1(2); // Line 1
person stardust    schedule 25.05.2013
comment
См. Ответ Стефано для дальнейшего обсуждения (Энди верен). explicit, вызывающий ошибку, означает, что они ни в коем случае не эквивалентны. - person chris; 25.05.2013
comment
Инициализация копированием не эквивалентна прямой инициализации. Первое концептуально достигается созданием временного объекта из инициализирующего выражения (2), а затем созданием перемещения или копирования obj1 из этого временного объекта. Однако компилятору разрешено исключать этот вызов конструктора перемещения или конструктора копирования в соответствии с 12.8/31 (см. мой ответ) - person Andy Prowl; 25.05.2013