C++ 11实现检查是否存在特定的成员函数

发布时间 - 2026-01-10 23:16:04    点击率:

问题提出

最近工作中遇到这样一个需求:实现一个ToString函数将类型T转换到字符串,如果类型T中含有同名方法ToString则直接调用。

这样一个ToString实现可以使用std::enable_if来做到,但是这里的难点在于如何判断类型T中存在这样一个ToString方法,以便可以放入enable_if中做SFINAE。

检查类中是否存在特定成员

相同的问题在知乎上有人提出过,@孙明琦的答案提供了一个用于检测特定检测子U在类型T下是否有效的检测器is_detected_v。其中用到了一个C++17的std::void_t,考虑到目前C++17还没得用,这个实现只作参考之用(事实上C++17自带了一个这样的检测器,并不需要自己写这样的模板)。

经人提醒,我参考了下标准库在实现swap上做的努力,看到了这样的写法:

namespace __swappable_details {
 using std::swap;
 
 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;
};
 
template <typename _Tp>
struct __is_swappable
 : public __is_swappable_impl<_Tp>::type
{};

简单分析可以看到__is_swappable被用来检查是否存在一个swap函数接受T作为参数,很有趣的是__test函数,如果存在swap函数满足条件,那么test(int)这个重载版本就会被选中。而如果不满足条件,因为推导失败就剩下了test(…)这个版本。通过这一手段,再设置下返回值分别为truefalse,就实现了这样的一个检测过程。

按图索骥,检查是否存在成员ToString的模板就可以这么写:

namespace details
{
 struct HasMemberToStringValidator
 {
  template <typename T, typename = decltype(&T::ToString)>
  static std::true_type Test(int);
 
  template <typename>
  static std::false_type Test(...);
 };
}
 
template <typename T>
struct HasMemberToString :
 public decltype(details::HasMemberToStringValidator::Test<T>(0))
{};

HasMemberToString::value就是T中是否存在该成员的计算结果。

检测是否存在特定成员函数

但是上述代码有个问题,如果类T中的ToString是个成员变量,上述检测也会返回true。

解决这一问题的手段是去调用T::ToString,如果这个ToString可以被调用并能生成返回值,就认为这是个成员函数(严谨的讲,这个过程是确认T::ToString是callable的,但是callable的玩意不一定就是成员函数,然而实际使用并不需要这样细分)。

这里的另一个问题是,因为ToString是成员函数,那么decltype(T::ToString())这种手段就行不通了,因为成员函数必须带对象进行调用。既然必须要一个对象,那么这里的解决方法就是用上declval来产生一个对象,再用decltype获取返回值类型。

按照这个思路,验证过程被改动成:

struct HasMemberToStringValidator
{
 template <typename T, typename U =
  typename std::decay<decltype(std::declval<T>().ToString())>::type,
  typename = typename std::enable_if<std::is_same<std::string, U>::value>::type>
 static std::true_type Test(int);
 
 template <typename>
 static std::false_type Test(...);
};

这个升级版本除了能检查是否存在成员函数ToString以外还对返回值做了限定,确保返回的是string。以此类推,还能检查返回是否是u16string、u32string。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。


# c  # 成员函数  # c++11新增的便利算法实例分析  # 结合C++11新特性来学习C++中lambda表达式的用法  # 结合C++11的新特性来解析C++中的枚举与联合  # C++第11版本中的一些强大的新特性小结  # C++11的新特性简单汇总介绍 (一)  # C++11的新特性简单汇总介绍 (二)  # 是否存在  # 返回值  # 的是  # 这一  # 这样一个  # 就会  # 是个  # 按图索骥  # 也会  # 有个  # 还能  # 以此类推  # 就行  # 可以看到  # 问题是  # 考虑到  # 分别为  # 可以使用  # 这篇文章  # 带了 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 中山网站推广排名,中山信息港登录入口?  在centOS 7安装mysql 5.7的详细教程  nodejs redis 发布订阅机制封装实现方法及实例代码  深入理解Android中的xmlns:tools属性  Laravel Vite是做什么的_Laravel前端资源打包工具Vite配置与使用  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】  LinuxCD持续部署教程_自动发布与回滚机制  打开php文件提示内存不足_怎么调整php内存限制【解决方案】  HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】  INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】  微信小程序制作网站有哪些,微信小程序需要做网站吗?  javascript日期怎么处理_如何格式化输出  如何快速查询网址的建站时间与历史轨迹?  用yum安装MySQLdb模块的步骤方法  Laravel与Inertia.js怎么结合_使用Laravel和Inertia构建现代单页应用  怎么用AI帮你为初创公司进行市场定位分析?  5种Android数据存储方式汇总  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】  Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】  Python结构化数据采集_字段抽取解析【教程】  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  如何在VPS电脑上快速搭建网站?  Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作  Internet Explorer官网直接进入 IE浏览器在线体验版网址  Laravel如何处理CORS跨域请求?(配置示例)  WEB开发之注册页面验证码倒计时代码的实现  Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理  Laravel如何实现API版本控制_Laravel版本化API设计方案  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  郑州企业网站制作公司,郑州招聘网站有哪些?  如何快速搭建高效WAP手机网站?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  浅谈Javascript中的Label语句  Android自定义listview布局实现上拉加载下拉刷新功能  原生JS获取元素集合的子元素宽度实例  Laravel如何实现一对一模型关联?(Eloquent示例)  lovemo网页版地址 lovemo官网手机登录  免费网站制作appp,免费制作app哪个平台好?  bing浏览器学术搜索入口_bing学术文献检索地址  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  zabbix利用python脚本发送报警邮件的方法  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  公司网站制作价格怎么算,公司办个官网需要多少钱?  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  HTML 中如何正确使用模板变量为元素的 name 属性赋值  简历没回改:利用AI润色让你的文字更专业  如何在建站之星绑定自定义域名?  常州企业网站制作公司,全国继续教育网怎么登录?