前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++17常用新特性(十二)---编译器的if语句

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

作者头像
CPP开发前沿
发布2022-06-04 17:31:05
8790
发布2022-06-04 17:31:05
举报
文章被收录于专栏:CPP开发前沿

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

代码语言:javascript
复制
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
复制
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
复制
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
复制
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
复制
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
复制
template <typename T>
auto GetValue(T t){
    if constexpr (std::is_integral<T>::value && T{} < 10) {
        return t + 2;
    }
    return t;
}

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

代码语言:javascript
复制
int main()
{
    cout<<GetValue("24")<<endl;
    return 0;
}

报错结果如下:

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

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

代码语言:javascript
复制
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
复制
int main()
{
    cout<<GetValue(12)<<endl;
    return 0;
}

输出结果为:

代码语言:javascript
复制
obj和T类型一致
1

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

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

代码语言:javascript
复制
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 删除。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档