未调用模板类复制构造函数

Template class copy constructor not called

本文关键字:复制 构造函数 调用      更新时间:2023-10-16

我的复制构造函数没有被调用,我不知道为什么。这是我的代码:

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,因此允许在ptr4拥有的内存中就地构造__tmp,就好像最初运行的实际代码只是:

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—结束示例 ]