news 2026/7/25 11:14:08

跟我学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/7/23 7:30:23

SELECT:开启数据世界的钥匙——3000字实战指南

SELECT&#xff1a;开启数据世界的钥匙——3000字实战指南据统计&#xff0c;95%的企业级应用存在SQL性能瓶颈&#xff0c;平均每增加1毫秒延迟导致年损失超百万。本文通过3000字深度解析&#xff0c;结合B树原理、电商案例、索引创建代码三要素&#xff0c;揭示SELECT如何成为…

作者头像 李华
网站建设 2026/7/23 7:32:00

轻量模型也能高性能?AnimeGANv2 CPU推理效率实测

轻量模型也能高性能&#xff1f;AnimeGANv2 CPU推理效率实测 1. 引言&#xff1a;AI二次元转换的轻量化突破 随着深度学习在图像生成领域的广泛应用&#xff0c;风格迁移技术逐渐从实验室走向大众应用。其中&#xff0c;AnimeGANv2 作为专为“照片转动漫”设计的生成对抗网络…

作者头像 李华
网站建设 2026/7/23 8:49:17

Java8 groupingBy从入门到精通:图解+示例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个Java学习示例程序&#xff0c;逐步演示Collectors.groupingBy的使用&#xff1a;1) 基础单字段分组&#xff1b;2) 多级分组(先按省份再按城市)&#xff1b;3) 分组后聚合…

作者头像 李华
网站建设 2026/7/24 22:53:46

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

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

作者头像 李华
网站建设 2026/7/17 18:01:21

基于Vector工具链的UDS 28服务测试方案

如何用Vector工具链精准测试UDS 28服务&#xff1f;实战全解析 你有没有遇到过这样的场景&#xff1a;在刷写ECU程序时&#xff0c;总线突然拥塞&#xff0c;诊断中断&#xff1b;或者想安静地读取DTC&#xff0c;却被一堆周期性报文干扰得无法响应&#xff1f;这时候&#xff…

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

环境隔离大师:每个项目独立镜像,再不怕依赖冲突

环境隔离大师&#xff1a;每个项目独立镜像&#xff0c;再不怕依赖冲突 1. 为什么需要环境隔离&#xff1f; 作为一名接外包的开发者&#xff0c;你是否经常遇到这样的困扰&#xff1a;同时维护5个项目时&#xff0c;conda环境乱到连自己都理不清&#xff1f;Python版本冲突、…

作者头像 李华