说说SFINAE

2023-11-14


大后天就是除夕了,牛年将过,虎年马上来了。以一篇关于C++的非常小众的文章作为牛年的结尾,若有讲的不清楚或讲错的地方欢迎大家留言指出来。为什么今年最后一篇文章是关于c++的呢?因为自己工作后用过的第一门语言便是c++,感谢。

SFINAE简介

SFINAESubstitution Failure is NOT an Error的首字母缩写,David Vandevoorde最先使用这个缩写来描述相关编程技术,可翻译为替换失败并非错误

维基百科中对此有讲解,举的例子也不错,可点击这里查看,本文关于SFINAE的例子来源于此百科。可是,今天有朋友反应看不太懂这个例子了,相信很多没有使用过c++11或者更高版本的朋友多少都有此种困惑,这里我们简单解说一下。

言归正传,当创建一个重载函数的候选集时,某些(或全部)候选函数是用模板实参替换(可能的推导)模板形参的模板实例化结果。如果某个模板的实参替换时失败,编译器将在候选集中删除该模板,而不是当作一个编译错误从而中断编译过程,这需要C++语言标准授予如此处理的许可。如果一个或多个候选保留下来,那么函数重载的解析就是成功的,函数调用也是良好的。

SFINAE的一个例子

通过一个简单例子了解一下SFINAE,

struct Test {
  typedef int foo;
};

template <typename T>
void f(typename T::foo) {}  // Definition #1

template <typename T>
void f(T) {}  // Definition #2

int main() {
  f<Test>(10);  // Call #1.
  f<int>(10);   // Call #2. Without error (even though there is no int::foo)
                // thanks to SFINAE.
}

在限定名字解析时(T::foo)使用非类的数据类型,导致f<int>推导失败,因为int并无嵌套数据类型foo, 但程序仍是良好定义的,因为候选函数集中还有一个有效的函数(Defination #2)!

虽然SFINAE最初引入时是用于避免在不相关模板声明可见时(如通过包含头文件)产生不良程序。许多有心的程序员后来发现这种行为可用于编译时内省introspection。具体说来,在模板实例化时允许模板确定模板参数的特定性质。

例如,SFINAE用于确定一个类型是否包含特定typedef

#include <iostream>

template <typename T>
struct has_typedef_foobar {
  // Types "yes" and "no" are guaranteed to have different sizes,
  // specifically sizeof(yes) == 1 and sizeof(no) == 2.
  typedef char yes[1];
  typedef char no[2];

  template <typename C>
  static yes& test(typename C::foobar*);

  template <typename>
  static no& test(...);

  // If the "sizeof" of the result of calling test<T>(nullptr) is equal to
  // sizeof(yes), the first overload worked and T has a nested type named
  // foobar.
  static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};

struct foo {
  typedef float foobar;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << has_typedef_foobar<int>::value << std::endl;  // Prints false
  std::cout << has_typedef_foobar<foo>::value << std::endl;  // Prints true
}

当类型T有嵌套类型foobartest的第一个定义被实例化并且空指针常量被作为参数传入。(结果类型是yes。)如果不能匹配嵌套类型foobar,唯一可用函数是第二个test定义,且表达式的结果类型为no。省略号(ellipsis)不仅用于接收任何类型,它的转换的优先级是最低的,因而优先匹配第一个定义,这去除了二义性。

使用C++11进行代码简化

在 C++11 中,上述代码可以简化为:

#include <iostream>
#include <type_traits>

template <typename... Ts>
using void_t = void;

template <typename T, typename = void>
struct has_typedef_foobar : std::false_type {};

template <typename T>
struct has_typedef_foobar<T, void_t<typename T::foobar>> : std::true_type {};

struct foo {
  using foobar = float;
};

int main() {
  std::cout << std::boolalpha;
  std::cout << has_typedef_foobar<int>::value << std::endl;
  std::cout << has_typedef_foobar<foo>::value << std::endl;
}

对于c++11代码的解说

  1. 要看懂上边的代码,首先要知道的第一点是std::false_typestd::true_type都是结构体,这两个结构体有bool型的成员变量,值分别为falsetrue,以下是其可能的实现。
typedef integral_constant<bool, false> false_type;
typedef integral_constant<bool, true> true_type;
template<class T, T v>
struct integral_constant {
    static constexpr T value = v;
    using value_type = T;
    using type = integral_constant; // using injected-class-name
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; } // since c++14
};
  1. 第二点就是在c++中结构体也是可以继承的,所以has_typedef_foobar完全继承false_type或者true_type,此外什么也没做。
  2. 为了便于理解,我们把上边的代码在编译器看来会是什么样子,写在下边
#include <iostream>
#include <type_traits>

template <typename... Ts>
using void_t = void;

template <typename T, typename = void>
struct has_typedef_foobar : std::false_type {};

template<>
struct has_typedef_foobar<int, void> : public std::integral_constant<bool, false>
{
};

template<>
struct has_typedef_foobar<foo, void> : public std::integral_constant<bool, true>
{
};

template<typename T>
struct has_typedef_foobar<T, void_t<typename T::foobar> > : public std::integral_constant<bool, true>
{
};

struct foo
{
  using foobar = float;
};

int main()
{
  std::cout.operator<<(std::boolalpha);
  std::cout.operator<<(std::integral_constant<bool, false>::value).operator<<(std::endl);
  std::cout.operator<<(std::integral_constant<bool, true>::value).operator<<(std::endl);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

说说SFINAE 的相关文章

  • 更新面板工作速度非常慢

    我正在编写一个用户可以注册的应用程序 注册时 可以选择多个选项 并根据这些注册字段可见或不可见以及是否必需 我想出了一个想法 所有字段都将位于 updatePanel 中 当用户更改注册选项时 我将在服务器端设置这些字段的可见性 它可以工作
  • 在 CPP 类中将 C 函数声明为友元

    我需要在 C 函数中使用类的私有变量 我正在做这样的事情 class Helper private std string name public std getName return name friend extern C void in
  • Grpc - 将消息从一个客户端发送到连接到同一服务器的另一个客户端

    是否可以将消息从一个客户端发送到连接到同一服务器的另一个客户端 我想将数据从一个客户端发送到服务器然后发送到特定客户端 我想我需要获取客户端 ID 但我不知道如何获取此 ID 以及如何从服务器将此消息发送到该客户端 我这里有一个样本 这是一
  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • 前向声明类型和“已声明为类类型的非类类型”

    我对以下代码有问题 template
  • 有些有助于理解“产量”

    在我不断追求少吸的过程中 我试图理解 产量 的说法 但我不断遇到同样的错误 someMethod 的主体不能是迭代器块 因为 System Collections Generic List 不是迭代器接口类型 这是我被卡住的代码 forea
  • 如何将 .txt 文件中的数据转换为 xml? C#

    我在一个文本文件中有数千行数据 我想通过将其转换为更容易搜索的内容来轻松搜索 我希望 XML 或其他类型的大型数据结构 尽管我不确定它是否是最好的对于我的想法 每行的数据如下所示 第 31 册 托马斯 乔治 32 34 154 每本书都不是
  • 在 C# 中,如何根据在 gridview 行中单击的按钮引用特定产品记录

    我有一个显示产品网格视图的页面 该表内有一列 其中有一个名为 详细信息 的超链接 我想这样做 以便如果用户单击该特定产品的详细信息单元格 将打开一个新页面 提供有关该产品的更多信息 我不确定如何确定哪个Product记录链接的详细信息以及我
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • 什么是空终止字符串?

    它与什么不同标准 字符串 http www cplusplus com reference string string 字符串 实际上只是一个数组chars 空终止字符串是指其中包含空字符的字符串 0 标记字符串的结尾 不一定是数组的结尾
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 如何递归取消引用指针(C++03)?

    我正在尝试在 C 中递归地取消引用指针 如果传递一个对象 那就是not一个指针 这包括智能指针 我只想返回对象本身 如果可能的话通过引用返回 我有这个代码 template
  • 在 C# 中为父窗体中的子窗体控件添加事件处理程序

    我有两种形式 一种是带有按钮和文本框的父表单 单击该按钮时 将打开一个对话框 该子窗体又包含一个文本框和一个按钮 现在我想要的是 每当子表单文本框中的文本更改时 父表单文本框中的文本会自动更改 为了获得这个 我所做的是 Form3 f3 n
  • 将函数参数类型提取为参数包

    这是一个后续问题 解包 元组以调用匹配的函数指针 https stackoverflow com questions 7858817 unpacking a tuple to call a matching function pointer
  • 如何最好地以编程方式将 `__attribute__ ((unused))` 应用于这些自动生成的对象?

    In my makefile我有以下目标 它将文本 HTML 资源 编译 为unsigned char数组使用xxd i http linuxcommand org man pages xxd1 html 我将结果包装在匿名命名空间和标头保
  • 如何在 C# 中创建异步方法?

    我读过的每一篇博客文章都会告诉您如何在 C 中使用异步方法 但由于某些奇怪的原因 从未解释如何构建您自己的异步方法来使用 所以我现在有这段代码使用我的方法 private async void button1 Click object se
  • Visual Studio 2015:v120 与 v140?

    仅供参考 Win10 x64 我今天开始尝试 Visual Studio 2015 在弄清楚如何运行 C C 部分后 我尝试加载一个大型个人项目 该项目使用非官方的glsdk http glsdk sourceforge net docs
  • WPF DataGrid / ListView 绑定到数组 mvvm

    我们假设你有 N 个整数的数组 表示行数的整数值 在模型中 该整数绑定到视图中的 ComboBox Q1 如何将数组 或数组的各个项目 绑定到 DataGrid 或 ListView 控件 以便 当您更改 ComboBox 值时 只有那么多
  • 在 System.Type 上使用条件断点时出错

    这是函数 public void Init System Type Type this Type Type BuildFieldAttributes BuildDataColumns FieldAttributes 我在第一行设置了一个断点
  • 我可以使用 lambda 函数或 std::function 对象来代替函数指针吗?

    我有一个需要使用的库 它定义了以下内容 typedef void CallbackFunction const int i 并且有一个注册回调的函数 如下所示 void registerCallback CallbackFunction p

随机推荐

  • 三层架构实现增删改查操作封装

    文章目录 概要 整体架构流程 技术名词解释 技术细节 小结 概要 三层架构 三层架构分为 数据 dao 层 业务 service 层 控制 controller 层 1 表示层 USL 即User Show Layer 视图层 a 前台 对
  • 学习材料收集

    记一个好帖子 http www wowotech net
  • 一个例子让你看清线程调度的随机性

    粉丝提问 c语言 如何定义一个和库函数名一样的函数 并在函数中调用该库函数 一个端口号可以同时被两个进程绑定吗 两个线程 两个互斥锁 怎么形成一个死循环 一个例子让你看清线程调度的随机性 线程调度的几个基本知识点 多线程并发执行时有很多同学
  • 【STM32】中断向量表

    我是通过这个进行学习的 我觉得讲的很好 这里我稍加修改 作为自己的学习笔记 嵌入式杂谈之中断向量表 前言 STM32根据boot引脚的配置方式有3种启动方式 但是无论哪一种方式 对于STM32来说都是从0x0000 0000启动 STM32
  • Ubuntu18下载安装IDEA最新版

    下载地址 官网地址 下载 选择功能更强大的一版 点击UItimate下面的Download 保存 TAR GZ压缩文件 解压到 opt文件下 找到压缩文件所在的文件夹 右键在终端打开 输入下面代码 记得改成自己文件包的名字 sudo tar
  • Spring之底层架构核心概念解析

    目录 一 BenDefinition 二 Spring定义Bean的方式 三 BeandefinitionReader 四 AnnotatedBeandefinitionReader 五 XmlBeanDefinitionReader 六
  • 修改NuGet包默认存放位置

    默认情况下 NuGet下载的包存放在系统盘 C盘中 这样一来 时间长了下载的包越多 C盘占用的空间也就越多 1 问题描述 默认情况下 NuGet下载的包存放在系统盘 C盘中 一般在路径C Users 用户 nuget packages下 这
  • 2021-05-23unity【OnEnable,  OnDisable,  OnDestroy】(这是三个方法函数)-(声明方法即可)(常用于设置游戏结束运行后的状态)   

    3 OnEnable OnDisable OnDestroy 这是三个方法函数 声明方法即可 常用于设置游戏结束运行后的状态 OnEnable 当对象变为可用或激活状态时 此函数被调用 注 这里的对象指的是 挂有该脚本的对象 OnDisab
  • python基于字典多线程目录枚举工具

    基于字典多线程目录枚举工具 整体思路 命令行参数获取 字典文件的读取 多线程访问 命令行参数获得 使用模块 sys getopt sys argv获取命令行执行的数据 参数获得 opt args getopt getopt sys argv
  • SQL group by和count

    group by 使用时具体看右边是要统计什么 统计什么就具体把那一列得数据给贴上去和左边对应 不使用聚合函数直接group by 分组 只截取对应分组第一行数据 group by可以通过逗号用多个字段进行分组 group by 字段1 字
  • 遇到问题之-centos安装配置hadoop超详细过程(含故障排除)

    1 集群部署介绍 1 1 Hadoop简介 201205161427003307 png Hadoop是Apache软件基金会旗下的一个开源分布式计算平台 以Hadoop分布式文件系统 HDFS Hadoop Distributed Fil
  • 个人对智能家居平台选择的思考

    本人之前开发过不少MicroPython程序 其中涉及到自动化以及局域网控制思路 也可以作为智能家居的实现方式 而NodeMCU ESPHome的方案具有方便添加硬件 容易更新程序和容量占用小的优势 本人也查看过相关教程后感觉部署ESPHo
  • 滤波器的主要参数

    一 3dB带宽定义和理解 3dB带宽指幅值等于最大值的二分之根号二倍时对应的频带宽度 幅值的平方即为功率 平方后变为1 2倍 在对数坐标中就是 3dB的位置了 也就是半功率点了 对应的带宽就是功率在减少至其一半以前的频带宽度 表示在该带宽内
  • 面试题亲身经历

    1 什么是浅拷贝和深拷贝有什么区别 1 浅拷贝只是复制了对象的引用地址 两个对象指向同一个内存地址 所以修改其中任意的值 另一个值都会随之变化 这就是浅拷贝 例 assign 2 深拷贝是将对象及值复制过来 两个对象修改其中任意的值另一个值
  • PPP协议使用同步传输技术传送比特串0110111111111100。试问经过零比特填充后变成怎样的比特串?若接收端收到的PPP帧的数据部分是0001110111110111110110,问删除发送端

    比特填充法的具体做法是 在发送端 当一串比特流尚未加上标志字段时 先用硬件扫描整个帧 只要发现5个连续1 则立即填入一个0 因此经过这种零比特填充后的数据 就可以保证不会出现6个连续1 在接收一个帧时 先找到F字段以确定帧的边界 接着再用硬
  • ADIDAS阿里纳斯励志广告语

    系列文章目录 精选优美英文短文1 Dear Basketball 亲爱的篮球 精选优美英文短文2 Here s to the Crazy Ones 致疯狂的人 我祝你不幸并痛苦 约翰 罗伯茨毕业致辞 亲爱的波特兰 CJ麦科勒姆告别信 Hi
  • 你现在是一名博主,你要写关于chatgpt的文章,有哪些独特见解,写500字

    作为一名 AI 模型 ChatGPT 的独特之处在于它能够生成高质量的文本内容 并且具有很强的语言理解能力 首先 ChatGPT 是一种基于 Transformer 架构的语言模型 它通过大量的训练数据学习了语言的知识和结构 这使得它能够生
  • 运放积分器为什么要在电容两端并联一个电阻,阻值怎么计算

    本文摘自 新概念模拟电路 对于积分电路 在实际应用中 因为存在输入失调电压 输入偏置电流 会导致会有一个持续电流流过反馈电容 使电容上的电压累积增加或者减小 最终达到最大输出电压 上图很好的解释了给电容充电的原因 为了避免这种现象 一般在电
  • LVGL V8之flex布局item反序排列

    flex布局实现 static void lv example flex 4 void lv obj t cont lv obj create lv scr act 当前活动界面上创建obj对象 lv obj set size cont 3
  • 说说SFINAE

    文章目录 SFINAE简介 SFINAE的一个例子 使用C 11进行代码简化 对于c 11代码的解说 大后天就是除夕了 牛年将过 虎年马上来了 以一篇关于C 的非常小众的文章作为牛年的结尾 若有讲的不清楚或讲错的地方欢迎大家留言指出来 为什