虽然auto_ptr已经过时了, 但是对这种古董做考古工作仍旧有很多裨益。
#include <iostream> #include <memory> using namespace std; struct S{ ~S(){ std::cout << "~S() called" << std::endl; } }; void foo( std::auto_ptr<S> ptr){ //ptr.release(); } int main () { std::auto_ptr<S> ptr(new S); foo(ptr); if (!ptr.get()){ std::cout << "ptr is null" ; } }
所有权移交(总是move),是auto_ptr的核心语义之一。当调用foo(ptr)后,会打印输出“ptr is null” 。
为了防止发生foo中的行为,可以定成:
1 void foo( const std::auto_ptr<S>& other){ 2 //other.release(); //compile error。 const& ptr 阻止了对ptr.release(). 3 }
那么auto_ptr中的拷贝构造函数是什么样子的?
auto_ptr( const auto_ptr& other); //这绝不可能,为了实现唯一所有权,我们需要对other.release(); 显然const阻止了这个事情的发生。
auto_ptr( auto_ptr& other); //正确的。
那么这又怎么解释?
std::auto_ptr<S> makeS(){ std::auto_ptr<S> ret(); return ret; } std::auto_ptr<S> a = makeS();
C++03时代,makeS()返回右值,右值只能传递给auto_ptr(const auto_ptr& other);或者 auto_ptr( auto_ptr&& other); 可惜这两个函数都不存在。
解决办法就是:auto_ptr_ref. 本质是个抓右值的工具(在C++03时代,是个开创性的发明)。右值std::auto_ptr<S>隐式转换成auto_ptr_ref<S>, 传递给template< class Y > auto_ptr( auto_ptr_ref<Y> m );
下面是模拟,先提出一个问题,是C++03无法编译的:
#include <iostream> using namespace std; struct S{ S(int i){ std::cout << "S(int i) called \n"; i_in_s_ = i; } S(S &s){ std::cout << "S(S s) called \n"; } int i_in_s_; }; int main(){ S b = S(1); }
还是那个T& 无法绑定右值的问题,解决如下:
#include <iostream>using namespace std; struct S{ struct S_ref{ S_ref(int i): i_(i){ } int i_; }; operator S_ref(){ std::cout << "operator S_ref called \n"; return S_ref(i_in_s_); } S(int i){ std::cout << "S(int i) called \n"; i_in_s_ = i; } S(S &s){ std::cout << "S(S s) called \n"; } S(S_ref r){ std::cout << "S(S_ref r) called \n"; i_in_s_ = r.i_; } int i_in_s_; }; int main(){ S b = S(1); }
解决。思想就是:抓右值的目的,无非就是想拷贝点东西出来吧。我先把东西拷贝到S_ref,然后再传给S(S_ref r)。也能实现传递数据的功能。
考古工作暂时告一段落。