Мой конструктор копирования не вызывается, и я не знаю почему. Вот мой код:
template <typename T>
class SmartPtr
{
public:
explicit SmartPtr(T *p) : m_p(p) { cout << "ctor" << endl; }
SmartPtr(const SmartPtr& p) : m_p(p.m_p) { cout << "copy ctor" << endl;}
private:
T* m_p;
};
int main()
{
SmartPtr<int> pt4 = SmartPtr<int>(new int);
}
Выход только «ctor». Похоже, используется конструктор копирования по умолчанию. Если я добавлю «явный», то он не скомпилируется, выдав ошибку:
"error: no matching function for call to ‘SmartPtr<int>::SmartPtr(SmartPtr<int>)’"
Что я здесь не так делаю?
Это то, что известно как Копировать Elision. Это хорошая оптимизация, когда копия явно не нужна. Вместо эффективного запуска кода:
SmartPtr<int> __tmp(new int);
SmartPtr<int> ptr4(__tmp);
__tmp.~SmartPtr<int>();
Компилятор может знать, что __tmp
существует только для строительства ptr4
и, следовательно, разрешено строить __tmp
на месте в памяти, принадлежащей ptr4
как будто фактический исходный код был просто:
SmartPtr<int> ptr4(new int);
Обратите внимание, что вы можете сказать компилятору НЕ делать этого тоже. Например, на gcc вы можете передать -fno-elide-constructors
и с этим единственным изменением (дополнительно регистрируя деструктор), теперь ваш код печатает:
ctor
copy ctor // not elided!
dtor
dtor // extra SmartPtr!
Увидеть демонстрация.
В стандарте §12.8:
Такое исключение операций копирования / перемещения, называемое разрешением копирования, допускается при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):
[Пример:
- В
return
оператор в функции с типом возвращаемого класса, когда …- В вбрасывание выражение, когда …
- когда временный объект класса, который не был связан со ссылкой (12.2), будет скопирован / перемещен
для объекта класса с таким же cv-неквалифицированным типом, операция копирования / перемещения может быть опущена
построение временного объекта непосредственно в цель пропущенного копирования / перемещения- когда Исключение декларирование обработчика исключений (статья 15) …
class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f();
Здесь критерии для исключения могут быть объединены, чтобы исключить два вызова конструктора копирования класса
Thing
:
копирование локального автоматического объекта t во временный объект для возвращаемого значения функцииf()
и копирование этого временного объекта в объектt2
, Эффективно, строительство местного объектаt
может рассматриваться как непосредственная инициализация глобального объектаt2
и уничтожение этого объекта произойдет в программе
выход. Добавление конструктора перемещения в Thing имеет тот же эффект, но это конструкция перемещения из
временный объектt2
это исключено. — конец примера ]