news 2026/5/23 12:50:48

跟我学C++中级篇—std::is_swappable手动实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟我学C++中级篇—std::is_swappable手动实现

一、说明

对象的交换在C++开发中非常常见,比如一些常见的排序算法中以及面试中字符串手动实现中都可以使用,典型的就是STL中的std::swap。如果在普通的编程中,交换两个对象还好控制。如果在模板编程中呢?可能一大片错误遮蔽了屏幕,这个有点不太合适。所以是不是可以增加一个类似于前面的is_xxx系列来判断类型对象不是可以交换呢?

二、std::is_swappable

C++17中为了确保在交换对象时的安全性和通用性,提供了一个元编程接口std::is_swappable用来在编译期判断当前的对象是否可以被交换。其返回一个布尔值,用来表示该类型对象是否可交换。其定义如下:

template<class T,class U>structis_swappable_with;template<class T>structis_swappable;template<class T,class U>structis_nothrow_swappable_with;template<class T>structis_nothrow_swappable;

实现的代码如下:

struct __do_is_swappable_impl { template<typename _Tp, typename = decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))> static true_type __test(int); template<typename> static false_type __test(...); }; template<typename _Tp> struct __is_swappable_impl : public __swappable_details::__do_is_swappable_impl { typedef decltype(__test<_Tp>(0)) type; }; /// is_swappable template<typename _Tp> struct is_swappable : public __is_swappable_impl<_Tp>::type { static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}), "template argument must be a complete class or an unbounded array"); };

这里先不对这些代码说明,等和下面的代码实现对比着分析就更明白了。
需要说明的是,标准库中还提供了is_nothrow_swappable、std::is_swappable_with和is_nothrow_swappable_with三个类似的元编程接口,std::is_swappable_with是用来比较两个类型是否可以交换,而std::is_swappable是当前一个类型是否可以交换。

三、源码实现

下面就实现一个普通代码的版本:

#include<type_traits>#include<utility>// swapusing std::swap;template<typename T,typename U,typename=void>structswappable_with_impl:std::false_type{};template<typename T,typename U>structswappable_with_impl<T,U,std::void_t<decltype(swap(std::declval<T&>(),std::declval<U&>())),decltype(swap(std::declval<U&>(),std::declval<T&>()))>>:std::true_type{};template<typename T,typename U>structis_nothrow_swappable_with_impl{private:template<typename TT,typename UU>staticautotest(int)->std::integral_constant<bool,noexcept(swap(std::declval<TT&>(),std::declval<UU&>()))&&noexcept(swap(std::declval<UU&>(),std::declval<TT&>()))>;template<typename,typename>staticstd::false_typetest(...);public:using type=decltype(test<T,U>(0));};// is_swappable_withtemplate<typename T,typename U>structis_swappable_with:swappable_with_impl<T,U>{};template<typename T,typename U>inlineconstexpr bool is_swappable_with_v=is_swappable_with<T,U>::value;// is_swappabletemplate<typename T>structis_swappable:is_swappable_with<T,T>{};template<typename T>inlineconstexpr bool is_swappable_v=is_swappable<T>::value;// is_nothrow_swappable_withtemplate<typename T,typename U>structis_nothrow_swappable_with:is_nothrow_swappable_with_impl<T,U>::type{};template<typename T,typename U>inlineconstexpr bool is_nothrow_swappable_with_v=is_nothrow_swappable_with<T,U>::value;// is_nothrow_swappabletemplate<typename T>structis_nothrow_swappable:is_nothrow_swappable_with<T,T>{};template<typename T>inlineconstexpr bool is_nothrow_swappable_v=is_nothrow_swappable<T>::value;

再看一个高版本的实现:

#include<type_traits>#include<utility>// 检查 swap 是否有效template<typename T,typename U,typename=void>structis_swappable_with_impl:std::false_type{};template<typename T,typename U>structis_swappable_with_impl<T,U,std::void_t<decltype(//std::declval<void(&)(T&,U&)noexcept(noexcept(swap(std::declval<T&>(),std::declval<U&>())))>(),std::declval<void(&)(U&,T&)noexcept(noexcept(swap(std::declval<U&>(),std::declval<T&>())))>())>>:std::true_type{};// 主模板template<typename T,typename U=T>structis_swappable:is_swappable_with_impl<T,U>{};// 辅助变量模板template<typename T,typename U=T>inlineconstexpr bool is_swappable_v=is_swappable<T,U>::value;

测试的代码:

#include<iostream>structSwapOK{intd;};voidswap(SwapOK&a,SwapOK&b){std::swap(a.d,b.d);}structSwapErr{SwapErr(constSwapErr&)=delete;intd;};intmain(){std::cout<<std::boolalpha;std::cout<<"SwapOK is: "<<is_swappable_v<SwapOK><<std::endl;std::cout<<"SwapErr is: "<<is_swappable_v<SwapErr><<std::endl;std::cout<<"int and short : "<<is_swappable_with_v<int,short><<std::endl;std::cout<<"STL int and double : "<<std::is_swappable_with_v<int,double><<std::endl;return0;}

代码的原理就是在编译时构造一个swap(std::swap)调用,如果这个调用是合法的,则std::is_swappable::value为true,反之为false。上面的代码中仍然使用了std::void_t的处理来控制类型T(或U)可以用于swap(如果自定义类需要手动实现swap)。具体使用decltype和declval来获取具体的类型进行判断(须满足交换律)。至于noexcept版本,只是增加对函数的noexcept控制实现即可。来获取其匹配的方法仍然是编译适配的为std::true_type,进而展开生成正确的返回值;否则直接返回std::false_type。
另外需要说明的是,上面的代码在实际应用时,建议增加相关的名空间控制,否则很容易和STL中的相关代码混淆,产生误判。

四、总结

这几篇针对元编程接口的实现,可以发现std::true_type和std::void_t在这其中起着重要的作用。通过它们几个整合应用,就可以实现一些重要的功能。另外,通过这些实现,也可以更好的理解和融会贯通元编程的知识。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 13:12:27

从PowerDesigner迁移实战:电商系统数据库设计案例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个电商系统数据库设计案例演示应用。功能包括&#xff1a;1.商品分类管理(三级分类) 2.用户权限分级(买家/卖家/管理员) 3.订单状态流转 4.支付记录跟踪 5.物流信息管理。要…

作者头像 李华
网站建设 2026/5/10 20:59:42

AnimeGANv2人脸优化原理揭秘:如何避免五官变形?

AnimeGANv2人脸优化原理揭秘&#xff1a;如何避免五官变形&#xff1f; 1. 技术背景与问题提出 随着深度学习技术的发展&#xff0c;风格迁移&#xff08;Style Transfer&#xff09;已成为图像处理领域的重要应用方向。其中&#xff0c;将真实人像转换为二次元动漫风格的需求…

作者头像 李华
网站建设 2026/5/12 23:21:13

5分钟验证:Ubuntu搜狗输入法快速安装原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请生成一个最小化的Ubuntu搜狗输入法安装验证原型。要求&#xff1a;1.只包含核心安装步骤 2.能在5分钟内完成验证 3.输出明确的成功/失败状态 4.占用系统资源最少 5.可扩展为完整…

作者头像 李华
网站建设 2026/5/11 12:12:25

用AI快速测试魔兽宏创意:10秒验证你的战术想法

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个宏命令沙盒环境&#xff1a;1)输入宏创意描述&#xff08;如被攻击时自动闪现冰箱&#xff09;2)AI即时生成可执行代码 3)3D角色模拟器展示效果 4)参数调整滑块&#xff0…

作者头像 李华
网站建设 2026/5/21 14:28:09

AnimeGANv2能否自定义风格?微调训练部署全流程

AnimeGANv2能否自定义风格&#xff1f;微调训练部署全流程 1. 引言&#xff1a;AI 二次元转换器 - AnimeGANv2 随着深度学习在图像生成领域的持续突破&#xff0c;AnimeGANv2 成为近年来最受欢迎的轻量级照片转动漫模型之一。它基于生成对抗网络&#xff08;GAN&#xff09;架…

作者头像 李华
网站建设 2026/5/1 3:29:10

亲测好用9个AI论文软件,研究生高效写作必备!

亲测好用9个AI论文软件&#xff0c;研究生高效写作必备&#xff01; AI 工具如何让论文写作更高效&#xff1f; 在研究生阶段&#xff0c;论文写作是一项既耗时又费力的任务。无论是选题、文献综述、数据分析&#xff0c;还是最终的润色和降重&#xff0c;每一步都需要大量的…

作者头像 李华