news 2026/4/16 13:02:11

跟我学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/4/16 12:57:48

企业级网络安全:NESSUS在金融系统的实战部署

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个金融行业专用的NESSUS配置向导应用&#xff0c;包含&#xff1a;1)符合等保要求的扫描模板 2)敏感系统扫描注意事项 3)自动化调度设置 4)误报过滤规则 5)合规报告生成。要…

作者头像 李华
网站建设 2026/4/15 9:37:20

告别混乱:Git Commit工作流效率提升指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个高效的Git Commit工作流系统&#xff0c;要求&#xff1a;1. 支持Commit模板快速填充 2. 集成代码变更自动检测 3. 提供实时语法检查 4. 支持团队规范校验 5. 一键式提交操…

作者头像 李华
网站建设 2026/4/14 12:44:02

Multisim14.0支持下的电路分析课程改革探索:深度剖析

当“电路”不再抽象&#xff1a;用 Multisim14.0 重塑电子教学的底层逻辑 你有没有过这样的经历&#xff1f;在电路分析课上&#xff0c;老师讲完RC充放电的时间常数公式 $\tau RC$&#xff0c;你在纸上推导得头头是道&#xff0c;可一旦问到“那它实际长什么样&#xff1f;”…

作者头像 李华
网站建设 2026/4/16 1:35:24

5分钟部署通义千问2.5-7B-Instruct,AutoDL云服务器一键启动

5分钟部署通义千问2.5-7B-Instruct&#xff0c;AutoDL云服务器一键启动 1. 引言&#xff1a;为什么选择 Qwen2.5-7B-Instruct&#xff1f; 在当前大模型快速迭代的背景下&#xff0c;如何在有限算力条件下实现高性能、可商用的大语言模型本地化部署&#xff0c;成为开发者和企…

作者头像 李华
网站建设 2026/4/12 20:58:53

VibeThinker-1.5B如何设置提示词?系统指令最佳格式分享

VibeThinker-1.5B如何设置提示词&#xff1f;系统指令最佳格式分享 1. 引言&#xff1a;小参数模型的推理潜力与提示工程的重要性 随着大模型技术的发展&#xff0c;研究者和开发者逐渐意识到&#xff0c;并非只有超大规模参数模型才能胜任复杂任务。微博开源的 VibeThinker-…

作者头像 李华