Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++17常用新特性(十二)---编译器的if语句

C++17常用新特性(十二)---编译器的if语句

作者头像
CPP开发前沿
发布于 2022-06-04 09:31:05
发布于 2022-06-04 09:31:05
97100
代码可运行
举报
文章被收录于专栏:CPP开发前沿CPP开发前沿
运行总次数:0
代码可运行

编程时通过在if语句中使用constexpr关键字就可以在编译期计算if语句中的表达式,然后决定if语句走到哪个分支,没有走到的分支虽然编译器也会对这部分的代码进行代码走查,但其实这些代码最终可能不会被生成或者说被编译器丢弃。如下面这段代码所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
std::string ToString(T x){
    if constexpr(std::is_same_v<T, std::string>) {
        return x; //如果是字符串走到这个分支
    }
    else if constexpr(std::is_arithmetic_v<T>) {
        return std::to_string(x); //如果是数值型走到这个分支
    }
    else {
        return std::string(x);
    }
}

如上面的代码,传入的参数类型T会在编译期和if各分支语句中的类型相比较,如果is_same_v返回值不为真,这条语句可能就会被编译器丢弃掉。

1 编译器if语句缘起

在上面的示例代码中,如果将if表达式前的constexpr关键字去掉,然后在对模版做如下实例化时,再次对程序进行编译和执行会产生什么效果呢?实例化代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    cout<<ToString(42)<<endl;
    cout<<ToString("Hello World")<<endl;
    return 0;
}

点击编译,编译器将会报错,报错内容为:

从上图可以看出,传入类型为整型时,会使代码在if语句和else语句后的表达式无效从而导致编译器失败。失败的原因是什么呢?

这是因为在去掉了constexpr关键字后,实例化模板时编译器会将整个模板函数作为一个整体,if语句表达式检查又是运行时特性,即使在模板函数中if语句表达式为false也要能够通过编译才行。

现在就可以理解了,加上constexpr之所以能够通过编译,是因为在编译期对于表达式的值进行计算后,如果为false就不会生成该段代码,所以能通过编译并输出正确结果。

还有一点需要注意的就是:即使在编译期部分分支代码被丢弃,但是也必须满足语法正确

2 使用编译期 if 语句

原则上可以在所有的if语句中使用表达式,但是也有限制,既不能将它代替预编译指令,不能在函数体之外进行使用。

2.1 编译期if语句影响函数返回值

如下面的代码所示,代码编译没有问题,但是在运行时函数结果返回会导致不确定性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
auto GetValue(T t){
    if constexpr(std::is_same_v<T,std::string>){
        return "Success";
    }
    else{
        return 0;
    }
}

上面这段代码在编译时总是成功的,但是运行时改函数调用每次返回的结果类型是不一样的,如果传入一个string型,将返回字符串,其他则返回0这个整型数据。

2.2 编译期if语句返回值return不能省

编译器if语句不能省略else语句的返回值,否则可能导致编译器报错。如下面的代码就有可能会产生上面的问题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
auto GetValue(T t){
    if constexpr(sizeof(int)==4){
        return "hello";
    }
    return 1;
}

int main()
{
    cout<<GetValue(34)<<endl;
    return 0;
}

上面的代码传入一个整型数据时,会同时满足函数中if语句和if之外的返回语句,因此编译器会因为同时返回两个类型而报错。报错内容为:

只需要稍微改动,就可以将上面的代码通过编译器并运行出正确的结果。如下代码所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
auto GetValue(T t){
    if constexpr(sizeof(int)==4){
        return "hello";
    }
    else{
       return 1; 
    }
}

通过比较可以得出,运行时的if语句可以将else省略放到外面,但是编译期的if语句不能这么使用,因为这可能导致函数返回两个不同的类型返回值从而导致编译失败。

2.3 编译期if语句中的复杂表达式

之前写代码时,习惯在if语句中进行&&、||的表达式运算,但如果在编译期if语句中还这么写的话可能就会导致错误了。因此,如果想要使用编译期if语句达到和运行时if语句相同的效果,就需要把if语句中的表达式进行拆分改成if嵌套语句进行使用。之所以这么做也是因为,在编译期if语句中编译时判断的是if语句的整体,需要所有的语法格式都正确,才能通过编译。就像下面这段代码一样。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <typename T>
auto GetValue(T t){
    if constexpr (std::is_integral<T>::value && T{} < 10) {
        return t + 2;
    }
    return t;
}

如上面的代码,如果传入一个整型数据时能够正常编译且输出t+2的结果,当传入一个字符串时编译器就会报错。如做如下实例化时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    cout<<GetValue("24")<<endl;
    return 0;
}

报错结果如下:

3 带初始化的编译期if语句

惊喜吧,if语句中也可以进行初始化了,使用方法也很简单,方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using namespace std;
template <typename T>
auto GetValue(T t){
    if constexpr (std::is_integral<T>::value) {
        if constexpr(auto obj=t;std::is_same_v<decltype(obj),T>){
            cout<<"obj和T类型一致"<<endl;
            return 1;
        }
        else{
            cout<<"obj和T类型不一致"<<endl;
            return 2;
        }
    }
    else{
        return "hello";
    }
}

上面的代码就是在编译期if语句中对局部变量进行初始化并判断类型,然后输出不同的结果,当实例化代码如下所示时,它的输出结果和预期的是一致的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    cout<<GetValue(12)<<endl;
    return 0;
}

输出结果为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
obj和T类型一致
1

4 普通函数中使用编译期if语句

if constexpr 可以在任何函数中使用,需要注意的是在普通函数中使用的时候需要保证if语句的各分支语句都是正确的,否则也会导致编译错误。如下面的代码就不能通过编译:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    if constexpr(std::numeric_limits<char>::is_signed) {
        static_assert(std::numeric_limits<char>::is_signed);
    }
    else {
        static_assert(!std::numeric_limits<char>::is_signed);
    }
    return 0;
}

上面这段代码是永远不会通过编译器编译的,因为在这段代码里同时只会有有一个断言是有效的。由此也能得出结果,在上面的模板示例中使用编译期if语句会将无效的代码丢弃,但是在普通函数中计时条件为假、语法正确也是不会丢弃的。这一点也是使用时需要注意的地方。

5 总结

本文是对编译器if语句做了一个理论上全面的介绍,可能还有一些没有说到的地方,欢迎大家留言评论。谢谢。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP开发前沿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++23中if consteval / if not consteval (P1938R3) 详解
在C++的发展历程中,编译时计算一直是一个重要的特性,它可以提高程序的性能和安全性。C++20引入了 consteval 和 std::is_constant_evaluated() 等特性,前者用于声明必须在编译期间完成调用的立即函数,后者用于检查当前是否处于常量求值上下文。而在C++23中,进一步引入了 if consteval 和 if not consteval 语法,使得在代码中区分编译时和运行时行为变得更加方便和灵活。本文将详细介绍 if consteval 和 if not consteval 的相关内容,包括语法规则、设计目的、实际应用场景等。
码事漫谈
2025/04/25
890
C++23中if consteval / if not consteval (P1938R3) 详解
C++17 新特性深入解析:constexpr 扩展、if constexpr 和 constexpr lambda
C++17 不仅增强了现有特性,还引入了一些全新的编程工具,极大地提升了代码的效率和表达力。在这篇文章中,我们将深入探讨 C++17 中与 constexpr 相关的三个重要特性:constexpr 的扩展用法、if constexpr 和 constexpr lambda。这些特性不仅改变了我们对编译时计算的理解,还为模板编程和高性能代码提供了更多可能性。
码事漫谈
2025/01/22
2120
C++17 新特性深入解析:constexpr 扩展、if constexpr 和 constexpr lambda
全面盘点17个C++17的高级特性
C++17是目前比较常用的版本之一,今天花时间来梳理一下17个重要特性,所有的特性也不止这么点。
公众号guangcity
2024/03/22
3.9K0
全面盘点17个C++17的高级特性
C++17常用新特性(十一)---折叠表达式
从C++17开始,可以使用二元操作符对形参包中的参数进行计算,这一特性主要针对可变参数模板进行提升。支持的二元操作符多达32个。例如,下面的函数将会返回传入的所有的参数的和。
CPP开发前沿
2022/06/04
1.6K0
C++17常用新特性
每次C++版本的发布都会带来很多新的特性,C++17也不例外,虽然有很多期待的特性没有包含进来,但是新增的特性依然挡不住它独特的魅力。
CPP开发前沿
2021/11/16
2.4K0
C++17常用新特性
未来已来:从SFINAE到concepts
也就是说,在fun()函数内部,将参数x赋值给一个string类型的v,但是在main()函数中 ,调用fun()函数时候传入了1,这个编译器会推导为int类型,那么把一个int类型赋值给string,编译器会报错。
高性能架构探索
2024/01/26
3040
未来已来:从SFINAE到concepts
C++20新特性个人总结
concept乃重头戏之一,用于模板库的开发。功能类似于C#的泛型约束,但是比C#泛型约束更为强大。
用户7886150
2021/02/04
2K0
【翻译】C++17的新特性简介
上一篇翻译了C++14的新特性简介,这篇就是Anthony Calandra在https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP17.md 中对C++17重要的新特性的简介。原文中有些地方写得不是很好理解所以对其做了少量修改
ZifengHuang
2020/07/29
3.1K0
【翻译】C++17的新特性简介
C++一行代码实现任意系统函数Hook!
导语 | 一句话实现系统API的Hook,参数记录以及数据过滤与修改,关注敏感数据本身而不是哪个API的哪个参数可能有敏感的需要处理的信息,写工具的时候想到上述能力可以借助模板实现,赶紧尝试了一下,也做个笔记分享供大家学习。 一、AnyCall (一)背景 一般来说所有ApiHook库都会需要提供一个与被HookApi相似/相同的Myxxx函数以实现参数访问,这里以BlackBone的LocalHook举例,其需要的是被HookApi的引用参数形式,如下所示: bool TestFu
腾讯云开发者
2022/07/21
1.4K0
C++一行代码实现任意系统函数Hook!
C++17常用新特性(六)---lambda表达式的扩展
从C++11起就引入了lambda表达式,C++14又对其进行了丰富,开始支持使用泛型lambda。到现在的C++17 lambda的功能又进行了扩展。在C++17新特性中,主要支持了以下两种场景:
CPP开发前沿
2022/04/13
1K0
C++17常用新特性(六)---lambda表达式的扩展
C++14新增特性汇总
C++14整体来说只是发行的一个小版本,在C++11大版本的基础上做了一些优化和缺陷的修复。C++14在2014年8月18日正式批准宣布,同年12月15日正式发布release版本。本文中将就变动部分做一个总结,有需要改进和提升的地方希望大家批评指正。
CPP开发前沿
2021/11/16
5310
C++17 在业务代码中最好用的十个特性
作者:jinshang,腾讯 WXG 后台开发工程师 自从步入现代 C++时代开始,C++语言标准形成了三年一个版本的惯例:C++11 标志着现代 C++的开端,C++14 在 11 的基础上查缺补漏,并未加入许多新特性,而 C++17 作为 C++11 后的第一个大版本,标志着现代 C++逐渐走向成熟。WXG 编译器升级到 gcc7.5 已有一段时间,笔者所在项目组也已经将全部代码升级到 C++17。在使用了 C++17 一年多之后,笔者总结了 C++17 在业务代码中最好用的十个特性。 注 1:本文只
腾讯技术工程官方号
2022/05/25
2.8K0
C++17 在业务代码中最好用的十个特性
[译]C++17, 语言核心层有哪些新的变化?
C++11, C++14, 以及 C++17. 我猜你已经看出了其中的命名模式: 今年(2017)的晚些时候,我们便会迎来新的C++标准(C++17). 今年的3月份, C++17已经达到了标准草案阶段. 在我深入新标准的细节之前, 让我们先来总体浏览一下C++17.(译注:作者的文章写于2017年初,当时C++17标准仍未正式发布)
用户2615200
2022/01/12
8970
[译]C++17, 语言核心层有哪些新的变化?
C++17常用新特性(五)---强制省略拷贝或传递未实质化的对象
从C++标准产生一直到C++17,C++标准一直在试图减少某些临时变量或者拷贝的操作,虽然经过优化后,可能在实际执行中不需要调用拷贝或者移动构造,但是它必须隐士或者显示存在,如下面的案例,如果在类中禁止编译器默认生成拷贝构造和移动构造函数,代码将不会被编译通过。
CPP开发前沿
2022/04/13
1.4K0
C++17常用新特性(五)---强制省略拷贝或传递未实质化的对象
C++17 模板新特性详解:从新手到进阶
C++ 模板编程一直是 C++ 的强大特性之一,但它也以复杂性著称。幸运的是,C++17 引入了许多新的模板特性,这些特性不仅简化了模板编程,还提高了代码的可读性和灵活性。即使你是模板编程的新手,这些新特性也能帮助你更快上手,写出更优雅的代码。本文将详细介绍以下几个关键的模板新特性:
码事漫谈
2025/01/23
1720
C++17 模板新特性详解:从新手到进阶
C++17逻辑魔法:std::conjunction、std::disjunction 与 std::negati剖析
C++ 作为一门强大且广泛应用的编程语言,其标准的不断演进为开发者带来了诸多便利和强大的功能。C++17 作为 C++ 发展历程中的一个重要版本,引入了许多实用的特性,进一步提升了 C++ 的编程体验和效率。在 C++17 众多的新特性中,类型特性相关的改进为模板元编程提供了更强大的工具。其中,std::conjunction、std::disjunction 和 std::negation 这三个模板工具在类型层面的逻辑运算中发挥着关键作用 ,它们能够帮助我们更简洁、高效地处理复杂的类型逻辑判断,无论是在日常的代码编写还是在大型项目的架构设计中,都有着广泛的应用场景。接下来,就让我们深入探究这三个模板工具的奥秘。
码事漫谈
2025/02/06
1810
C++17逻辑魔法:std::conjunction、std::disjunction 与 std::negati剖析
C++23新特性—if consteval 编译时优化
C++就像一个不断成长的巨人,一方面不断的吸收各种优秀的设计思想,另一方面也在自身也在不断地进行优化,从开始到现在,C++已经发布了多个版本,每次版本的发布的时候都会给我们带来惊喜。目前C++23已经落地,本文要说的一个新的特性也是23版本中新增的,针对编译的优化委员会也是一直不遗余力,每发布一个版本,都会有相应的改进。
CPP开发前沿
2023/10/24
7710
C++23新特性—if consteval 编译时优化
C++17中新特性
从c++11开始,auto关键字能够通过初始化器推导出变量的类型。在c++14中,auto关键字的能力进一步提升,能够通过return语句推导出函数的返回类型。
XZAN
2019/01/14
5K0
Modern c++快速浅析
•template<typename T> void func(T& param);在这个示例函数中,如果传递进是一个const int&的对象,那么T推导出来的类型是const int,param的类型是const int&。可见引用性在型别推导的过程中被忽略•template<typename T> void func(T param);在这个示例函数中,我们面临的是值传递的情景,如果传递进的是一个const int&的对象,那么T和param推导出来的类型都是int如果传递进的是一个const char* const的指针,那么T和param推导出来的类型都是const char*,顶层const被忽略。因为这是一个拷贝指针的操作,因此保留原指针的不可更改指向性并没有太大的意义
高性能架构探索
2024/01/03
3070
Modern c++快速浅析
【翻译】C++14的新特性简介
之前写完了《C++Primer》的笔记,但是《C++Primer》已经是快十年的老书了,其包含的C++特性仅仅到C11为止,因此又去看了些C++14的特性,发现Anthony Calandra在https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP14.md 中有对C++14重要的新特性的简介,看完就翻译整理后发上来了。原文中有些地方写得不是很好理解所以对其做了少量修改。
ZifengHuang
2020/07/29
4.1K0
【翻译】C++14的新特性简介
相关推荐
C++23中if consteval / if not consteval (P1938R3) 详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验