Почему конструктор копирования вызывается дважды в этом фрагменте кода?
Я играю с несколькими вещами, чтобы понять, как работают конструкторы копирования. Но я не могу понять, почему конструктор копирования вызывается дважды для создания x2
. Я бы предположил, что он будет вызван один раз, когда возвращаемое значение createX()
копируется в x2
.
Я также рассмотрел несколько связанных вопросов по SO, но, насколько я могу судить, я не смог найти тот же простой сценарий, который я задаю здесь.
Кстати, я -fno-elide-constructors
с -fno-elide-constructors
, чтобы увидеть, что происходит без оптимизации.
#include <iostream>
struct X {
int i{2};
X() {
std::cout << "default constructor called" << std::endl;
}
X(const X& other) {
std::cout << "copy constructor called" << std::endl;
}
};
X createX() {
X x;
std::cout << "created x on the stack" << std::endl;
return x;
}
int main() {
X x1;
std::cout << "created x1" << std::endl;
std::cout << "x1: " << x1.i << std::endl << std::endl;
X x2 = createX();
std::cout << "created x2" << std::endl;
std::cout << "x2: " << x2.i << std::endl;
return 0;
}
Это вывод:
default constructor called
created x1
x1: 2
default constructor called
created x on the stack
copy constructor called
copy constructor called
created x2
x2: 2
Может ли кто-нибудь помочь мне, что я пропускаю или пропускаю здесь?
Ответы
Ответ 1
Здесь вы должны помнить, что возвращаемое значение функции - это отдельный объект. Когда вы делаете
return x;
Вы копируете инициализируемый объект возвращаемого значения с x
. Это первый вызов конструктора копирования, который вы видите. затем
X x2 = createX();
использует возвращенный объект для копирования инициализации x2
так что это вторая копия, которую вы видите.
Стоит отметить, что
return x;
постараюсь переместить x
в возвращаемый объект, если это возможно. Если бы вы сделали конструктор перемещения, вы бы увидели, что это называется. Причина этого заключается в том, что, поскольку локальные объекты выходят из области видимости в конце функции, компилятор обрабатывает объект как значение r, и только если он не находит допустимой перегрузки, он возвращается к возвращению его как lvalue.
Ответ 2
Первый экземпляр взамен createX
X createX() {
X x;
std::cout << "created x on the stack" << std::endl;
return x; // First copy
}
Второй - создать x2 из временного возврата createX.
X x2 = createX(); // Second copy
Обратите внимание, что в С++ 17 вторая копия вынуждена быть исключенной.