news 2026/3/14 9:27:42

跟我学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/3/13 20:56:41

手把手教学:通义千问2.5-7B-Instruct在AutoDL的完整部署流程

手把手教学&#xff1a;通义千问2.5-7B-Instruct在AutoDL的完整部署流程 1. 引言 随着大模型技术的快速发展&#xff0c;本地化或云端私有部署已成为开发者和企业应用大模型的重要方式。通义千问2.5-7B-Instruct作为阿里云于2024年9月发布的中等体量指令微调模型&#xff0c;凭…

作者头像 李华
网站建设 2026/3/13 9:40:16

5分钟快速验证JDK警告解决方案的原型方法

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个快速原型工具&#xff0c;允许开发者快速测试针对OpenJDK VM警告的不同解决方案。工具应提供预配置的测试环境&#xff0c;支持一键切换不同的JDK版本和配置&#xff0c;实…

作者头像 李华
网站建设 2026/3/12 22:16:21

Cursor新手指南:5分钟上手AI编程

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个适合新手的Cursor入门项目&#xff0c;创建一个简单的网页计算器。教程应分步指导如何安装Cursor、使用AI生成HTML/CSS/JavaScript代码&#xff0c;以及如何调试和运行项目…

作者头像 李华
网站建设 2026/3/14 16:51:24

【Java毕设全套源码+文档】基于springboot的个人健康档案管理系统设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/13 23:26:25

AI如何自动生成ZYFUN配置源接口代码

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请开发一个ZYFUN配置源接口服务&#xff0c;要求&#xff1a;1. 支持HTTP GET请求 2. 接收type和id两个必填参数 3. 根据type参数返回不同格式的JSON数据&#xff1a;当type1时返回…

作者头像 李华
网站建设 2026/3/14 1:01:51

小白指南:三步获取2025年最新免费学习资料

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个极简的资料获取向导应用&#xff0c;通过三个步骤引导用户&#xff1a;1) 选择领域&#xff08;下拉菜单包含编程、设计、语言学习等&#xff09; 2) 设置筛选条件&#x…

作者头像 李华