精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
服务方向
联系方式
锐英源精品原创,禁止转载和任何形式的非法内容使用,违者必究。点名“简易百科”和“闲暇巴”盗用锐英源原创内容。
最近讲了httplib源代码里的enable_if,后来又找到了一个更好的文章,分享给大家。但是本文主要还是讲if constexpr,结合if constexpr能更好理解enable_if。
在 C++17 中,出现了表达式 'if constexpr'。新功能的工作方式与常规if - else构造一样。它们之间的区别在于,条件表达式的值是在编译时确定的。
为了更好地说明 'if constexpr' 结构对 C++17 来说是多么伟大的创新,让我们看看在以前的语言标准中,类似的功能是如何用实现的:
#include <iostream> #include <string> template <typename T> std::enable_if_t<std::is_same<T, int>::value, void> TypeParam_Cpp11(T x) { std::cout << "TypeParam_Cpp11: int : " << x << std::endl; } template <typename T> std::enable_if_t<std::is_same<T, double>::value, void> TypeParam_Cpp11(T x) { std::cout << "TypeParam_Cpp11: double : " << x << std::endl; } template <typename T> std::enable_if_t<std::is_same<T, std::string>::value, void> TypeParam_Cpp11(T x) { std::cout << "TypeParam_Cpp11: std::string : " << x << std::endl; } int main() { TypeParam_Cpp11(5.9); TypeParam_Cpp11(10); TypeParam_Cpp11(std::string("577")); return 0; }
上述代码的输出如下:
TypeParam_Cpp11: double : 5.9 TypeParam_Cpp11: int : 10 TypeParam_Cpp11: std::string : 577
上述三个TypeParam_Cpp11函数的实现看起来很简单。只有以下表达式std::enable_if_t <condition, type>使函数复杂化(C++14 添加了std::enable_if_t .它只是一个访问::type的别名,::type在std::enable_if_t里)如果条件是真(即,仅当 std::is_same是真, T 和 U是同一类型)std::enable_if具有public成员typedef类型,否则,不引用任何内容。因此,在任何给定时间,该条件只能用于三种 implementations 中的一种。
当编译器看到具有相同名称的不同模板函数并且必须选择其中一个时,一个重要的原则开始发挥作用:它由缩写 SFINAE (Substitution Failure Is Not An Error) 表示。在这种情况下,这意味着如果无法根据无效的模板表达式推断其中一个函数的返回值(即,当std::enable_if_t条件为 false时),编译器不会生成错误。它只会继续工作并尝试处理其他函数实现。这就是全部的秘密。呃......好大惊小怪!
现在让我们使用新的 C++17 功能(即if constexpr表达式)更改上述实现,它允许我们在编译时做出决策来简化代码:
#include <iostream> #include <string> template <typename T> void TypeParam_Cpp17(T x) { if constexpr (std::is_same_v<T, int>) { std::cout << "TypeParam_Cpp17: int : " << x << std::endl; } else if constexpr (std::is_same_v<T, double>) { std::cout << "TypeParam_Cpp17: double : " << x << std::endl; } else if constexpr (std::is_same_v<T, std::string>) { std::cout << "TypeParam_Cpp17: std::string : " << x << std::endl; } } int main() { TypeParam_Cpp17(5.9); TypeParam_Cpp17(10); TypeParam_Cpp17(std::string("577")); return 0; }
上面的例子使用了条件判断。constexpr-if-else块具有多个条件,在编译时完成分支判断。为了确定参数 'x' 的类型,TypeParam函数使用std::is_same_v<A, B>表达式。如果 A 和 B属于同一类型,则表达式std::is_same_v<A, B>的计算结果为布尔值 true,否则值为false
总的来说,编译器从程序生成的所有运行时代码都不会包含与 'if constexpr' 条件相关的其他分支。这些机制的工作方式似乎与用于替换文本的#if 和#else preprocessor 宏相同,但在此结构中,整个代码甚至不需要语法正确。'if constexpr' 构造的分支必须在语法上有效,但未使用的分支在语义上不必正确。
最后一句话很重要,这是参与编译阶段的关键字,不是运行阶段的关键字,要理解这个,看下图:
在87行上其实也有断点,但是在执行时,这个断点就没有触发,相当于调用string分支时,其它分支是虚的,没参与编译。