C++核心准则C.83为何建议非平凡可拷贝值类自定义swap?
何时为C++类自定义swap()函数?
核心困惑点
C11起std::swap采用移动语义,对持有深层资源的类也能高效完成交换,通常直接用标准版本就足够。有人建议仅当类不可移动但需支持交换时才自定义swap,但C核心准则C.83却提出:只要是非平凡可拷贝的值类类型,就应考虑提供noexcept的swap函数。这和Stack Overflow上“移动语义出现后自定义swap已属特例”的说法冲突,容易让人困惑。
逐一拆解分析
std::swap的默认效率(C++11+)
默认std::swap会调用类的移动构造和移动赋值操作,本质是三次移动(临时对象移动构造,两次移动赋值),完全避免深拷贝。只要你的类正确实现了移动语义,这个过程已经足够高效,大部分场景下不需要额外自定义。自定义swap的传统与必要场景
在移动语义出现前,自定义swap是为了绕过深拷贝,直接交换类内部的资源指针(比如管理动态数组的类)。但移动语义普及后,这个需求被覆盖,只有当类不可移动(比如遗留类、或因业务逻辑禁用移动操作)但必须支持交换时,才需要自定义swap——这是当前最明确的必要场景。C++核心准则C.83的出发点
C.83的建议并非强制,而是从两个角度给出优化方向:- 极致效率:对于非平凡可拷贝的值类,自定义swap可以直接交换内部成员(比如仅交换两个指针),比三次移动操作更高效;
- 异常安全:自定义swap可以显式标记为
noexcept,而默认std::swap的noexcept属性依赖于类的移动操作是否noexcept。容器(如std::vector)在扩容等操作中,会依赖swap的noexcept来保证强异常安全。
实际应用规则
- 若类已实现noexcept的移动构造/赋值,直接用
std::swap即可,无需自定义; - 若类不可移动但需支持交换,必须自定义swap;
- 若类是非平凡可拷贝的值类,且追求极致效率或必须保证swap的
noexcept属性,可自定义swap——这是优化选项,而非强制要求。
内容的提问来源于stack exchange,提问作者Drue
相关产品推荐
相关产品推荐

