Вот простой заголовочный файл класса и основная программа. В основной программе я думал, что конструктор копирования вызывается ровно в трех ситуациях: инициализация (явное копирование), передача по значению для аргументов функции и возврат по значению для функций. Тем не менее, кажется, что это не вызывается для одного из них, я думаю, (3) или (4), как пронумеровано в комментариях. Для каких чисел (1) — (4) он вызывается? Благодарю.
X.h:
#include <iostream>
class X
{
public:
X() {std::cout << "default constructor \n";}
X(const X& x) { std::cout << "copy constructor \n";}
};
Главный:
#include "X.h"
X returnX(X b) // (1) pass by value - call copy constructor?
{
X c = b; // (2) explicit copy - call copy constructor?
return b; // (3) return by value - call copy constructor?
}
int main()
{
X a; // calls default constructor
std::cout << "calling returnX \n\n";
X d = returnX(a); // (4) explicit copy - call copy constructor?
std::cout << "back in main \n";
}
Выход:
default constructor
calling returnX
copy constructor
copy constructor
copy constructor
back in main
Поскольку копирование часто происходит в C ++ и, поскольку оно может быть дорогим, компилятору разрешается исключать определенные конструкции копирования (и перемещения). Это разрешение на копирование разрешено, даже если у конструктора elided и / или деструктора есть побочные эффекты, такие как выходные данные в вашей программе (то есть, на самом деле это не оптимизация, поскольку поведение с разрешением копирования и без него отличается).
В соответствии с пунктом 12.8 [class.copy] параграфа 12.8 может быть применено четыре основных места:
return
оператор при прямом возврате локальной переменной, которая имеет тот же тип, что и тип возврата функции.throw
заявление копия автоматической переменной внутри самой внутренней try
-блок может быть исключен, когда объект брошен.catch
предложение ловит объект по значению и с тем же типом, что и объект в throw
Заявление копия может быть опущена.Точные правила немного сложнее, но я думаю, что это суть. Учитывая, что правила исключения из копий достаточно строги, легко исключить возможность копирования: самый простой способ — это использовать identity()
функция:
template <typename T>
T const& identity(T const& object) {
return object;
}
...
X d = identity(returnX(a));
(эта версия также запрещает построение перемещения, хотя; выводит тип, используя T&&
и его возвращение должно сделать возможным создание перемещения, но я не совсем уверен, каким должен быть тип return и оператор return).
Номер 4 копирует временный. Это кандидат на копия elision. То есть при определенных условиях компилятору разрешается исключать вызовы конструктора копирования, даже если конструктор копирования имеет побочные эффекты.