C++03中能否实现非可拷贝对象的移动交换语义?
在C++03里给不可拷贝类实现"移动"效果:能做和不能做的事
嘿,这个问题问到点子上了——C03确实没有C11那种原生的移动语义,但咱们可以用点小技巧搞定一部分场景,不过有个场景确实是无解的,下面给你掰扯清楚:
先明确前提:不可拷贝类怎么写
首先,你说的不可拷贝类,在C++03里一般是把拷贝构造和拷贝赋值设成private(而且不写实现),让编译器没法偷偷生成默认版本,比如:
class MyClass { private: // 禁止拷贝,声明但不实现 MyClass(const MyClass&); MyClass& operator=(const MyClass&); // 举个例子,持有动态分配的资源 int* m_data; public: MyClass() : m_data(new int(0)) {} ~MyClass() { delete m_data; } };
第一种场景:test = returnMyClass()——可以搞定!
这种赋值操作,我们可以用**交换技巧(Swap Idiom)**模拟移动语义,核心思路是:不拷贝对象的资源,直接交换两个对象的资源指针,开销几乎为零。具体怎么做呢?
- 给
MyClass加个公开的swap成员函数,专门用来交换内部资源:
void swap(MyClass& other) { std::swap(m_data, other.m_data); // 只交换指针,不碰实际数据 }
- 因为C++03里临时对象不能绑定到非const左值引用,直接返回
MyClass临时对象的话,没法直接用swap。所以我们搞个移动代理类,让returnMyClass()返回这个代理,再给MyClass加个接受代理的赋值运算符:
class MyClass { // ... 之前的private成员、构造析构 ... public: // 移动代理:帮我们传递临时MyClass的所有权 class MoveProxy { public: MyClass* m_obj; explicit MoveProxy(MyClass* obj) : m_obj(obj) {} ~MoveProxy() { delete m_obj; } // 代理销毁时清理对象,除非所有权已经转移 }; static MoveProxy returnMyClass() { MyClass* temp = new MyClass(); // 这里给temp填充实际数据 *temp->m_data = 42; return MoveProxy(temp); } // 接受代理的赋值运算符:交换资源,完成"移动" MyClass& operator=(MoveProxy proxy) { swap(*proxy.m_obj); proxy.m_obj = nullptr; // 让代理别删已经转移的对象 return *this; } // ... swap函数 ... };
这样在main里写test = MyClass::returnMyClass()时,全程只有资源指针的交换,完全没有拷贝操作,达到了你要的效果。
第二种场景:MyClass test = returnMyClass()——真的搞不定!
你猜的没错,这种拷贝初始化的写法在C++03标准下是绝对行不通的,原因很简单:
- 哪怕你的编译器支持命名返回值优化(NRVO)——一种能把函数里的临时对象直接构造在函数外目标位置的优化,理论上可以跳过拷贝,但C++03有个硬性要求:代码必须在没有任何优化的情况下也能合法编译。
- 没有优化的话,
MyClass test = returnMyClass()会触发拷贝构造函数,把返回的临时对象拷贝到test里。但你的MyClass是不可拷贝的(拷贝构造是private),编译器直接就报错了,根本轮不到优化上场。
说白了,从语言标准层面,这种写法对于C03的不可拷贝类就是非法的,没办法绕过去,得等C11引入右值引用和移动构造才能解决这个问题。
最后总结一下
- 赋值操作(
test = returnMyClass()):可以通过Swap+代理的技巧模拟移动,避免拷贝,只要稍微改改类的实现就行。 - 拷贝初始化(
MyClass test = returnMyClass()):C03里真的做不到,必须升级到C11才能用原生移动语义搞定。
内容的提问来源于stack exchange,提问作者gentooise
相关产品推荐
相关产品推荐

