现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34196 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

14 | SFINAE:不是错误的替换失败是怎么回事?

使用 true_type 和 false_type 来选择合适的重载
将任意类型映射到 void,用于检查类型的有效性
用于在某个操作有效的情况下启用某个函数
可以选择性地启用某个函数的重载
试验文中的代码,加深对这些概念的理解
SFINAE 的重要性有降低的趋势,但仍需掌握其基本概念
标签分发
void_t
decltype 返回值
enable_if
has_reserve 模板可以检测一个类是否有一个名叫 reserve、参数类型为 size_t 的成员函数
SFINAE 可以用于编译期检测类的特性
如果没有找到最佳匹配,或者找到多个匹配程度相当的函数,则编译器需要报错
在生成的可行函数集合中,编译器会寻找一个最佳匹配,产生对该函数的调用
对于适用的函数模板,要根据实际情况对模板形参进行替换
根据名称找出所有适用的函数和函数模板
课后思考
内容小结
SFINAE 模板技巧
编译期成员检测
函数模板的重载决议
SFINAE: Substitution Failure Is Not An Error

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
我们已经连续讲了两讲模板和编译期编程了。今天我们还是继续这个话题,讲的内容是模板里的一个特殊概念——替换失败非错(substitution failure is not an error),英文简称为 SFINAE。

函数模板的重载决议

我们之前已经讨论了不少模板特化。我们今天来着重看一个函数模板的情况。当一个函数名称和某个函数模板名称匹配时,重载决议过程大致如下:
根据名称找出所有适用的函数和函数模板
对于适用的函数模板,要根据实际情况对模板形参进行替换;替换过程中如果发生错误,这个模板会被丢弃
在上面两步生成的可行函数集合中,编译器会寻找一个最佳匹配,产生对该函数的调用
如果没有找到最佳匹配,或者找到多个匹配程度相当的函数,则编译器需要报错
我们还是来看一个具体的例子(改编自参考资料 [1])。虽然这例子不那么实用,但还是比较简单,能够初步说明一下。
#include <stdio.h>
struct Test {
typedef int foo;
};
template <typename T>
void f(typename T::foo)
{
puts("1");
}
template <typename T>
void f(T)
{
puts("2");
}
int main()
{
f<Test>(10);
f<int>(10);
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了C++模板编程中的SFINAE(Substitution Failure Is Not An Error)概念及其应用。文章首先讨论了函数模板的重载决议过程,并介绍了SFINAE的最初用法。随后详细讨论了SFINAE在编译期成员检测中的应用,包括使用enable_if模板和has_reserve模板来选择性地启用函数重载,以及利用decltype返回值和void_t模板来避免错误的重载。通过这些技巧,读者可以在C++模板编程中灵活地进行类型检测和函数重载的控制。文章内容涵盖了C++模板编程中的高级技术,对于想要深入了解模板元编程的读者具有很高的参考价值。 总结:本文深入介绍了C++模板编程中SFINAE的概念及其应用,包括函数模板的重载决议过程和SFINAE在编译期成员检测中的灵活应用。通过学习本文内容,读者可以掌握在C++模板编程中灵活进行类型检测和函数重载控制的技巧。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(29)

  • 最新
  • 精选
  • 三味
    emmmm.... 这一节内容如果是半年前看到,应该能节省我好多时间去写序列化,真是我实实在在的需求啊! 我自己在写数据序列化为json文本的时候,就遇到了这样头疼的问题:如何根据类型,去调用对应的函数。 如果是简单的int,bool,float,直接特化就好了。 如果是自定义的结构体呢?我的做法就是判断自定义结构体中是否有serializable和deserializable函数,就用到了文中最开始的方法判断。 然而那会儿我写得还是太简单粗暴,在代码中用的是if去判断,对于不支持的类型,直接报错,并不能做到忽略。 看了本文之后,真是受益颇多啊!留言于此,告诉大家,别以为用不到这些内容,都是实实在在的干货!

    作者回复: 我喜欢这样的留言。哈哈,写专栏就是希望能给大家帮助的。😇

    2019-12-30
    34
  • 禾桃
    请问有编译器本身什么工具或者日志模式,可以显示模版实例化的过程?

    作者回复: 新发现一个工具,可以展示实例化的过程。你可以去看一下: https://cppinsights.io/

    2019-12-27
    2
    16
  • 好好虚度时光
    表示真的太烧脑了

    作者回复: 这个网站也许可以帮你: https://cppinsights.io/

    2020-01-17
    2
    6
  • _呱太_
    刚更新的时候看得头晕目眩,发现基础不够。回头抽空撸完了 STL 标准库 和 effective modern C++,回过头来看豁然开朗,真实受益匪浅

    作者回复: 欢迎走上一个台阶。学习无止境……

    2020-04-09
    5
  • 西加加
    看完了两课之后,正儿八经的想把各种 type_traits 用起来了。

    作者回复: 在合适的地方使用,记住目标是简化最终代码就行。模板代码容易让人脑袋疼是事实,限制主要用在底层的支持代码里。上层的使用代码一定要简单为好。

    2020-05-16
    3
  • 常振华
    标签分发那里的: template void append(C& container, T* ptr, size_t size) { _append( container, ptr, size, integral_constant< bool, has_reserve<C>::value>{});} 这个integral_constant< bool, has_reserve<C>::value>{}看不明白 integral_constant是上一讲的 template <class T, T v> struct integral_constant { static const T value = v; typedef T value_type; typedef integral_constant type; }; 吧? has_reserve<C>::value的::value是啥意思?

    作者回复: 看看下面的例子,是不是可以了? integral_constant<int, 1>::value == 1 integral_constant<int, 2>::value == 2 true_type::value == integral_constant<bool, true>::value == true

    2021-10-13
    2
  • geek
    老师, template <typename T, typename = void_t<>> struct has_reserve : false_type {}; template <typename T> struct has_reserve< T, void_t<decltype( declval<T&>().reserve(1U))>> : true_type {}; 第二个是一个类特化,但我理解,第二个中void的模板参数的推导结果和第一个是一样的。那此处 更特殊 这个意思是体现在 void的模板参数推导过程(无错)吗?

    作者回复: 不是所有类型都能满足第二个模板。

    2021-03-08
    2
  • Simon
    typename = void_t<> 这个写法是什么意思?类型名是不重要的?

    作者回复: 等效于 typename = void。类型参数名不重要。这样,特化的版本和通用的版本,后一项都是 void,不用调用者提供。 通用模板形式的第二个参数如果不是 void,那特化形式就永远不能匹配成功了,除非调用者手工提供第二项 void(第二项 void_t 的结果仍是 void)。

    2022-08-01归属地:广东
    1
  • 李亮亮
    template <typename T, typename = void_t<>> struct has_reserve : false_type {}; 这里的冒号是什么语法?

    作者回复: 继承啊。

    2019-12-30
    2
    1
  • 用 void_t 实现的 has_reserve 函数有一些问题,它没法判断类型的reserve 方法的返回值是否是 void,所以可以修改一下: template <typename T, typename = void> struct has_reserve : std::false_type {}; template <typename T> struct has_reserve<T, std::__void_t<decltype(std::declval<T&>().reserve(1U))>> : std::is_void<decltype(std::declval<T&>().reserve(1U))> {};

    作者回复: 看你关心什么了。我不关心/要求 reserve 没有返回值。 另外,不要使用 __void_t。这不是标准的一部分(是某些编译器私用的)。

    2024-03-10归属地:北京
收起评论
显示
设置
留言
29
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部