2

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

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>)’"

我在这里做错了什么?

4

1 回答 1

1

这就是所谓的复制省略。这是一个很好的优化,显然不需要副本。而不是有效地运行代码:

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具有类返回类型的函数的语句中,当...
  • throw-expression中,当...
  • 当尚未绑定到引用 (12.2) 的临时类对象将被复制/移动到具有相同 cv-unqualified 类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动
  • 当异常处理程序的异常声明(第 15 条)...

[示例:

class Thing {
public:
    Thing();
    ~Thing();
    Thing(const Thing&);
};

Thing f() {
    Thing t;
    return t;
}

Thing t2 = f();

这里可以结合省略的标准来消除对类的复制构造函数的两次调用Thing:将本地自动对象 t 复制到临时对象中以获得函数的返回值,f() 以及将该临时对象复制到 objectt2中。实际上,本地对象的构造t 可以看作是直接初始化全局对象t2,并且该对象的销毁将在程序退出时发生。将移动构造函数添加到 Thing 具有相同的效果,但它是从临时对象到的移动构造t2被省略了。—结束示例]

于 2015-01-15T12:53:17.713 回答