c++ constexpr

发布时间 2023-04-09 20:14:56作者: _Explosion!

c++中,constexpr 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证被声明的表达式在编译期就应该是一个常量表达式。

声明为constexpr的变量一定是一个const变量,而且必须用常量表达式初始化:

constexpr int mf = 20;  //20是常量表达式
constexpr int limit = mf + 1; // mf + 1是常量表达式
constexpr int sz = size(); //之后当size是一个constexpr函数时才是一条正确的声明语句

需注意constexpr和const在指针上的区别,constexpt只能对指针本身生效,而不针对指针所指向对象

const int*p = nullptr;        //p是一个指向整形常量的指针
constexpr int* q = nullptr;   //q是一个指向整数的常量指针

更具体的例子如下

#include <iostream>
#define LEN 10

int len_foo() {
    int i = 2;
    return i;
}
constexpr int len_foo_constexpr() {
    return 5;
}

constexpr int fibonacci(const int n) {
    return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}

int main() {
    char arr_1[10];                      // 合法
    char arr_2[LEN];                     // 合法

    int len = 10;
    // char arr_3[len];                  // 非法

    const int len_2 = len + 1;
    constexpr int len_2_constexpr = 1 + 2 + 3;
    // char arr_4[len_2];                // 非法
    char arr_4[len_2_constexpr];         // 合法

    // char arr_5[len_foo()+5];          // 非法
    char arr_6[len_foo_constexpr() + 1]; // 合法

    std::cout << fibonacci(10) << std::endl;
    // 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
    std::cout << fibonacci(10) << std::endl;
    return 0;
}

上例中,len_2虽然是const修饰的常数,但是其不能用来初始化一个数组的大小,因为c++标准里数组的长度必须是一个常量表达式,而不是常数,该行为是ub的。

此外constexpr修饰的函数可用递归,c++14后可在constexpr函数内使用局部变量、循环和分支等简单语句。

巧妙的使用constexpr可用减少代码量,见下例

static std::string generate(const Node& node) {
            return std::visit(        //c++17 std::visit(T,val),使用对象class/泛型Lambdas/重载的Lambdas方式访问val
                [](auto&& arg)->std::string {
                    using T = std::decay_t<decltype(arg)>;    //c++14std::decay_t标记c++11std::decay 类型消除,将数据带有的const,&等修饰符关键字去除
                    if constexpr (std::is_same_v<T, Null>) {
                        return "null";
                    }
                    else if constexpr (std::is_same_v<T, Bool>) {
                        return arg ? "true" : "false";
                    }
                    else if constexpr (std::is_same_v<T, Int>) {
                        return std::to_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, Float>) {
                        return std::to_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, String>) {
                        return generate_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, Array>) {
                        return generate_array(arg);
                    }
                    else if constexpr (std::is_same_v<T, Object>) {
                        return generate_object(arg);
                    }
                },
                node.value);
        }

其中结构体node的成员value是std::variant类型,上例代码使用if constexpr,使得程序在编译器去检测if的合法性,免去了重载多个类型的函数。(二者效果相同)

 

参考文章:https://blog.csdn.net/janeqi1987/article/details/103542802

https://changkun.de/modern-cpp/zh-cn/02-usability/