unique_ptr<T>不允许复制构造,相反,它支持移动语义。但是,我可以从函数返回unique_ptr<T>并将返回值赋给变量。
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> foo()
{
unique_ptr<int> p( new int(10) );
return p; // 1
//return move( p ); // 2
}
int main()
{
unique_ptr<int> p = foo();
cout << *p << endl;
return 0;
}
上面的代码按预期进行编译和工作。那么,为什么第1行没有调用复制构造函数并导致编译器错误呢?如果我不得不使用第2行,那就有意义了(使用第2行也可以,但我们不需要这样做)。
我知道c++ 0x允许unique_ptr这个异常,因为返回值是一个临时对象,一旦函数退出就会被销毁,从而保证了返回指针的唯一性。我很好奇这是如何实现的,它在编译器中是特殊的,还是在语言规范中有一些其他的子句,这利用了?
在语言规范中是否有其他子句可以利用?
是的,见12.8§34和§35:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...]
This elision of copy/move operations, called copy elision, is permitted [...]
in a return statement in a function with a class return type, when the expression is the name of
a non-volatile automatic object with the same cv-unqualified type as the function return type [...]
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue,
overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
只是想再补充一点,按值返回应该是这里的默认选择,因为在最坏的情况下,return语句中的命名值,即在c++ 11、c++ 14和c++ 17中没有省略的情况下,被视为右值。例如,下面的函数使用-fno-elide-constructors标记进行编译
std::unique_ptr<int> get_unique() {
auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
return ptr; // <- 2, moved into the to be returned unique_ptr
}
...
auto int_uptr = get_unique(); // <- 3
通过在编译时设置标志,这个函数中会发生两次移动(1和2),然后再发生一次移动(3)。