C++ 的 SFINAE(Substitution Failure Is Not An Error) 机制和类型萃取(type traits)

C++ 的 SFINAE(Substitution Failure Is Not An Error) 机制和类型萃取(type traits)

flyfish

Substitution Failure Is Not An Error 翻译为中文是“替换失败不是错误”。它是 C++ 模板编程中的一个重要概念,指在模板实例化过程中,如果某些模板参数的替换导致编译失败,编译器不会报错,而是会尝试其他可行的模板特化或重载。
从简单的开始介绍

模板编程

模板编程是一种在C++中创建泛型代码的方式。模板允许编写与类型无关的函数和类,可以在编译时通过不同的类型实例化。

template<typename T>
T add(T a, T b) {return a + b;
}int main() {int x = add(3, 4);        // 实例化为 add<int>double y = add(2.5, 3.5); // 实例化为 add<double>return 0;
}

模板实例化

模板实例化是将模板参数替换为具体类型的过程。实例化可以是隐式的(由编译器在使用模板时自动完成)或显式的(程序员手动指定)。

template<typename T>
T multiply(T a, T b) {return a * b;
}// 显式实例化
template int multiply(int, int);int main() {int x = multiply(3, 4); // 使用显式实例化return 0;
}

模板特化

模板特化允许为特定类型提供不同于通用模板的实现。特化有两种形式:部分特化(Partial Specialization)和完全特化(Full Specialization)。

完全特化

完全特化是指为模板的特定类型提供一个完整的实现,覆盖掉通用模板的实现。这意味着特化的模板只适用于特定的类型。

示例
假设我们有一个通用的模板类 MyClass,并且我们想为 int 类型提供一个特化的实现:

#include <iostream>template<typename T>
class MyClass {
public:void print() {std::cout << "Generic template\n";}
};// 完全特化
template<>
class MyClass<int> {
public:void print() {std::cout << "Specialized for int\n";}
};int main() {MyClass<double> obj1;obj1.print(); // 输出: Generic templateMyClass<int> obj2;obj2.print(); // 输出: Specialized for intreturn 0;
}

部分特化

部分特化是指为模板的某些类型参数提供一个特化的实现,而不是全部。这在类模板中尤为常见,因为函数模板不支持部分特化。

示例
假设我们有一个模板类 MyClass,并且我们想为特定类型参数组合提供一个特化的实现:
MyClass<double, char> 使用通用模板实现。
MyClass<int, char> 使用部分特化,当第一个类型参数是 int 时的实现。
MyClass<char, double> 使用部分特化,当第二个类型参数是 double 时的

#include <iostream>// 通用模板
template<typename T, typename U>
class MyClass {
public:void print() {std::cout << "Generic template\n";}
};// 部分特化,当第一个类型参数是 int 时
template<typename U>
class MyClass<int, U> {
public:void print() {std::cout << "Specialized for int and U\n";}
};// 部分特化,当第二个类型参数是 double 时
template<typename T>
class MyClass<T, double> {
public:void print() {std::cout << "Specialized for T and double\n";}
};int main() {MyClass<double, char> obj1;obj1.print(); // 输出: Generic templateMyClass<int, char> obj2;obj2.print(); // 输出: Specialized for int and UMyClass<char, double> obj3;obj3.print(); // 输出: Specialized for T and doublereturn 0;
}

完全特化意味着为模板的所有参数都提供了具体的类型,从而创建了一个针对这些具体类型的模板实例。这通常是在所有模板参数都是已知类型时使用。

部分特化则是在模板的一些参数是已知类型,而其他参数仍然保持泛型的情况下,提供一个特化的版本。这允许为某些参数的特定组合提供专门的实现,而其他参数保持泛型。例如有一个模板类,可以为其中一个参数是 int 的组合重写部分功能,而其他参数可以是任意类型

回到要说的SFINAE 机制

SFINAE 机制

在 C++ 中,模板允许编写通用代码,但有时模板的某些实例化并不适用于所有类型。SFINAE 机制允许编译器在模板参数替换失败时,不报错,而是继续寻找其他匹配的模板特化或重载。

编译器尝试其他模板特化或重载
当模板替换失败时,编译器会尝试:

其他重载函数:如果一个函数模板的实例化失败,编译器会尝试查找并使用其他可能匹配的重载函数。
其他模板特化:如果一个模板特化的实例化失败,编译器会尝试查找并使用其他匹配的模板特化。

SFINAE(Substitution Failure Is Not An Error)

SFINAE 是 C++ 模板机制的一部分,它表示在模板实例化过程中,如果替换模板参数失败,不会导致编译错误,而是让编译器尝试其他模板特化或重载。这通常用于实现条件模板和类型萃取。

下面这个例子中,hasFoo 函数模板在 T 具有成员函数 foo 时启用,否则尝试重载函数。通过 decltype 和 std::true_type 或 std::false_type 来判断类型是否具有 foo 成员函数。

#include <iostream>
#include <type_traits>// 条件函数模板:当 T 具有成员函数 foo 时启用
template<typename T>
auto hasFoo(T t) -> decltype(t.foo(), std::true_type{}) {return std::true_type{};
}// 条件函数模板:当 T 没有成员函数 foo 时启用
std::false_type hasFoo(...) {return std::false_type{};
}class A {
public:void foo() { std::cout << "A::foo()" << std::endl; }
};class B {};int main() {std::cout << std::boolalpha;std::cout << "A has foo: " << decltype(hasFoo(A{}))::value << std::endl;std::cout << "B has foo: " << decltype(hasFoo(B{}))::value << std::endl;return 0;
}

类型萃取(type traits)

萃读作cui,四声。
类型萃取是一组模板类和模板函数,用于在编译时获取类型信息或进行类型操作。类型萃取用于查询和操作类型的属性。C++ 标准库提供了许多类型萃取工具,例如 std::is_void、std::is_integral、std::is_pointer 等。标准库中的 std::is_integral 就是一个常见的类型萃取。
“萃取”一词来源于“萃”字,有收集、提炼之意。在类型萃取的上下文中,“萃取”可以理解为“抽取”或“提取”,即从类型中抽取其固有的特性或属性。类似于化学上的萃取——从混合物中提取出特定成分。

#include <iostream>
#include <type_traits>template<typename T>
void checkType() {if (std::is_integral<T>::value) {std::cout << "Type is integral." << std::endl;} else {std::cout << "Type is not integral." << std::endl;}if (std::is_pointer<T>::value) {std::cout << "Type is pointer." << std::endl;} else {std::cout << "Type is not pointer." << std::endl;}
}int main() {checkType<int>();      // 输出:Type is integral.//       Type is not pointer.checkType<int*>();     // 输出:Type is not integral.//       Type is pointer.checkType<double>();   // 输出:Type is not integral.//       Type is not pointer.return 0;
}

结合 SFINAE 和类型萃取来实现条件编译和类型操作

#include <iostream>
#include <type_traits>// 条件模板函数:仅在 T 是整型时启用
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void printType(T value) {std::cout << "Integral type: " << value << std::endl;
}// 条件模板函数:仅在 T 是浮点型时启用
template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
void printType(T value) {std::cout << "Floating-point type: " << value << std::endl;
}int main() {printType(42);         // 输出:Integral type: 42printType(3.14);       // 输出:Floating-point type: 3.14// printType("hello"); // 编译错误:没有匹配的函数模板return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Bootstrap 导航栏:设计、定制与实现

Bootstrap 导航栏&#xff1a;设计、定制与实现 Bootstrap 是一个流行的前端框架&#xff0c;它提供了丰富的组件和工具&#xff0c;帮助开发者快速构建响应式和移动优先的网页。在 Bootstrap 的众多组件中&#xff0c;导航栏&#xff08;Navbar&#xff09;是一个核心元素&am…

linux配置podman阿里云容器镜像加速器

1.下载podman yum install -y podman systemctl status podman systemctl start podman 2.获取阿里云个人容器镜像加速器地址 访问阿里云官网&#xff1a;首先&#xff0c;您需要访问阿里云&#xff08;Alibaba Cloud&#xff09;的官方网站。阿里云官网的URL是&#xff1a;…

使用 Arthas 进行 Base64 编码和解码

引言 在处理数据传输和存储时&#xff0c;经常需要对数据进行编码和解码。Base64 是一种常用的编码方式&#xff0c;它可以将二进制数据转换成64个可打印的字符&#xff0c;从而方便在文本环境中传输和存储。Arthas 提供了强大的 Base64 编码和解码功能&#xff0c;使得在 Jav…

OS:处理机进程调度

1.BackGround&#xff1a;为什么要进行进程调度&#xff1f; 在多进程环境下&#xff0c;内存中存在着多个进程&#xff0c;其数目往往多于处理机核心数目。这就要求系统可以按照某种算法&#xff0c;动态的将处理机CPU资源分配给处于就绪状态的进程。调度算法的实质其实是一种…

Django实战项目之进销存数据分析报表——第二天:项目创建和 PyCharm 配置

在上一篇博客中&#xff0c;我们讨论了如何搭建一个全栈 Web 应用的开发环境&#xff0c;包括 Python 环境的创建、Django 和 MySQL 的安装以及前端技术栈的选择。现在&#xff0c;让我们继续深入&#xff0c;学习如何在 PyCharm 中创建一个新的 Django 项目并进行配置。 一…

汇编 -- ARM汇编之 .inst指令与udf指令使用

ARM 汇编中的 .inst 与 udf 指令 技术背景 在ARM汇编编程中&#xff0c;有时需要使用一些标准汇编语言不支持的特殊指令&#xff0c;或需要在代码中插入断点或生成故意的异常以便进行调试和错误处理。.inst和udf指令在这些场景中非常有用。 .inst 指令 语法 .inst <mac…

对于品牌方来说,小红书探店应该怎么做?

小红书是中国最大的生活分享社交平台之一&#xff0c;它现在不仅仅是一个购物推荐平台&#xff0c;也是一个探店的好去处。 用户在网络上看到一家心仪的店铺&#xff0c;却又无法亲身到访&#xff0c;对店铺的产品存疑&#xff0c;这时候就会在小红书搜索具体的相关店铺信息。 …

MacOS解决安装pycurl的问题 no such file or directory: ‘/usr/lib/libcurl.@libext@‘

背景 芯片&#xff1a;MacBook Pro M3Mac OS: 14.1Python 2.7pip20.3.4 执行pip install pycurl7.19.0总是报错&#xff0c;例如&#xff1a; Looking in indexes: http://mirrors.aliyun.com/pypi/simple/ Collecting pycurl7.19.0Downloading http://mirrors.aliyun.com/p…

BUG ImportError: cannot import name ‘QAction‘ from ‘PySide6.QtWidgets‘

BUG ImportError: cannot import name ‘QAction’ from ‘PySide6.QtWidgets’ 环境 PySide6 6.7.2详情 在参考 PyQt5 的代码写 Pyside6 的右键菜单时遇到的错误。 错误代码 from PySide6.QtWidgets import QAction错误原因&#xff1a; 在PySdie6中&#xf…

【代码随想录】【算法训练营】【第58天 4】 [卡码104]建造最大岛屿

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 58&#xff0c;周四&#xff0c;ding~ 题目详情 [卡码104] 建造最大岛屿 题目描述 卡码104 建造最大岛屿 LeetCode类似题目827 最大人工岛 解题思路 前提&#xff1a; 思路&#xff1a; 重点…

Rust编程-模式匹配

模式&#xff1a; 用来匹配类型结构 组成&#xff1a;字面量、解体的数组、元组、枚举、结构体 、变量、通配符、占位符等组件组成 模式匹配的作用&#xff1a; 模式与match或其他工具配合使用可以更好控制流程 match分支&#xff1a; match 值 {模式 > 表达式 } match分支…

【LeetCode】从前序与中序遍历序列构造二叉树

目录 一、题目二、解法完整代码 一、题目 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9…

在西藏上大学是一种什么体验?如何解决语言问题?

在西藏地区上大学是一种独特而难忘的经历&#xff0c;它不仅提供了接触壮丽自然风光和深入了解藏族文化的机会&#xff0c;也带来了适应高原气候和生活方式的挑战。学生将在这里体验到丰富的教育资源和学术研究素材&#xff0c;同时在生活和人际交往中培养适应能力和独立性。这…

密码学基础-Hash、MAC、HMAC 的区别与联系

密码学基础-Hash、MAC、HMAC 的区别与联系 Hash Hash 是一种从一段数据中创建小的数字“指纹”的方法。就像一个人的指纹代表一个人的信息一样&#xff0c;Hash 对输入的数据进行整理&#xff0c;生成一个代表该输入数据的“指纹” 数据。通常该指纹数据也可称之为摘要、散列…

文件包含漏洞: 函数,实例[pikachu_file_inclusion_local]

文件包含 文件包含是一种较为常见技术&#xff0c;允许程序员在不同的脚本或程序中重用代码或调用文件 主要作用和用途&#xff1a; 代码重用&#xff1a;通过将通用函数或代码段放入单独的文件中&#xff0c;可以在多个脚本中包含这些文件&#xff0c;避免重复编写相同代码。…

RabbitMQ的学习和模拟实现|Protobuf的介绍和简单使用

protbuf的介绍和简单使用 项目仓库&#xff1a;https://github.com/ffengc/HareMQ protobuf的安装&#xff1a;README-cn.md#环境配置 基于Protobuf的一个小项目&#xff1a;基于protobuf和httplib的在线通讯录项目框架&#xff5c;Protobuf应用小项目 protobuf是什么 Pro…

TinyVue:与 Vue 交往八年的组件库

本文由体验技术团队莫春辉老师原创~ 去年因故停办的 VueConf&#xff0c;今年如约在深圳举行。作为东道主 & 上届 VueConf 讲师的我&#xff0c;没有理由不来凑个热闹。大会结束后&#xff0c;我见裕波在朋友圈转发 Jinjiang 的文章《我和 Vue.js 的十年》&#xff0c;我就…

openlayers 3d 地图 非三维 立体地图 行政区划裁剪 地图背景

这是实践效果 如果没有任何基础 就看这个专栏&#xff1a;http://t.csdnimg.cn/qB4w0 这个专栏里有从最简单的地图到复杂地图的示例 最终效果&#xff1a; 线上示例代码&#xff1a; 想要做这个效果 如果你的行政区划编辑点较多 可能会有卡顿感 如果出现卡顿感需要将边界点相应…

Python爬虫-淘宝搜索热词数据

前言 本文是该专栏的第70篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者有详细针对“亚马逊Amazon搜索热词”数据采集的详细介绍,对此感兴趣的同学,可以往前翻阅《Python爬虫-某跨境电商(AM)搜索热词》进行查看。 而在本文,笔者将以淘宝为例,获取…

【扩散模型(五)】IP-Adapter 源码详解3-推理代码

系列文章目录 【扩散模型&#xff08;一&#xff09;】中介绍了 Stable Diffusion 可以被理解为重建分支&#xff08;reconstruction branch&#xff09;和条件分支&#xff08;condition branch&#xff09;【扩散模型&#xff08;二&#xff09;】IP-Adapter 从条件分支的视…