C++中拷贝构造函数私有/受保护的类能否使用返回值移动语义?
背后原理
这个问题是C03到C11的语法兼容性,以及移动构造的隐式生成规则共同导致的:
- 旧版C++规范里禁止类型拷贝的常规做法是把拷贝构造函数声明为private且不实现,你用到的CGAL Kd_tree用的就是这种写法。
- 按照C++标准,只要类自定义了拷贝构造函数(无论访问权限是什么),编译器都不会自动生成默认的移动构造函数和移动赋值运算符。
- 你返回局部对象
tree时,编译器首先会尝试匹配移动构造,找不到移动构造就会回退到拷贝构造,而拷贝构造是私有成员,自然会触发编译报错。 - 哪怕实际编译时会触发*RVO(返回值优化)*省略掉拷贝/移动操作,语法层面仍然要求对应的构造函数是可访问的,所以哪怕优化后不需要调用,也过不了编译检查。
你当前可以使用的解决方法
- 方案1:用智能指针包装返回值,完全规避PointTree本身的拷贝/移动需求
#include <memory> std::unique_ptr<PointTree> preparePointTree(const vector<PlyPoint>& pointCloud) { auto tree = std::make_unique<PointTree>(); kd_tree::prepare<...>(pointCloud, *tree); return tree; } // 调用方式 auto tree = preparePointTree(pointCloud);
- 方案2:改用输出参数传递结果,写法虽然不够简洁但兼容性最高
void preparePointTree(const vector<PlyPoint>& pointCloud, PointTree& out_tree) { kd_tree::prepare<...>(pointCloud, out_tree); } // 调用方式 PointTree tree; preparePointTree(pointCloud, tree);
- 方案3:如果你可以修改本地CGAL头文件,可以直接给Kd_tree添加public的移动构造,之后就可以按你原来的写法返回对象。
CGAL库侧的适配方案
CGAL只需要把原有禁止拷贝的写法,替换为C++11之后的标准写法,就可以同时实现禁止拷贝、支持移动的需求:
public: // 显式声明移动操作为默认实现,允许移动 Kd_tree(Kd_tree&&) = default; Kd_tree& operator=(Kd_tree&&) = default; // 显式删除拷贝操作,禁止拷贝 Kd_tree(const Kd_tree&) = delete; Kd_tree& operator=(const Kd_tree&) = delete;
替换原有private拷贝构造的写法后,用户侧的返回值写法就可以正常编译,不需要额外修改。
内容的提问来源于stack exchange,提问作者nh2
相关产品推荐
相关产品推荐

