未调用模板 class 复制构造函数
Template class copy constructor not called
我的复制构造函数没有被调用,我不确定为什么。这是我的代码:
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"。看起来使用了默认的复制构造函数。如果我添加 "explicit" 然后它不会编译,给出错误:
"error: no matching function for call to ‘SmartPtr<int>::SmartPtr(SmartPtr<int>)’"
我做错了什么?
这就是所谓的Copy Elision。这是一个很好的优化,显然不需要副本。而不是有效地 运行ning 代码:
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!
参见 demo。
在标准中,§12.8:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- In a
return
statement in a function with a class return type, when ...
- In a throw-expression, when ...
- when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move
- when the exception-declaration of an exception handler (Clause 15) ...
[Example:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing
:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2
. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2
, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2
that is elided. —end example ]
我的复制构造函数没有被调用,我不确定为什么。这是我的代码:
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"。看起来使用了默认的复制构造函数。如果我添加 "explicit" 然后它不会编译,给出错误:
"error: no matching function for call to ‘SmartPtr<int>::SmartPtr(SmartPtr<int>)’"
我做错了什么?
这就是所谓的Copy Elision。这是一个很好的优化,显然不需要副本。而不是有效地 运行ning 代码:
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!
参见 demo。
在标准中,§12.8:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- In a
return
statement in a function with a class return type, when ...- In a throw-expression, when ...
- when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
- when the exception-declaration of an exception handler (Clause 15) ...
[Example:
class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class
Thing
: the copying of the local automatic object t into the temporary object for the return value of functionf()
and the copying of that temporary object into objectt2
. Effectively, the construction of the local objectt
can be viewed as directly initializing the global objectt2
, and that object’s destruction will occur at program exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the temporary object tot2
that is elided. —end example ]