c++ constexpr
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函数内使用局部变量、循环和分支等简单语句。c++17中支持if constexpr,其可以在实例化模板时直接去除条件不成立的分支。
template<typename T> auto func(T val) { if constexpr (std::is_same_v<T, char*>) { std::cout << *val << endl; } else { std::cout << val << endl; } } int main() { char *p = new char('a'); int k = 1; func(k); func(p); return 0; }
//输出结果 1
// a
上例中在执行func(k)时,func会被直接实例化为 std::cout << val << endl;,func(p)则会被实例化为std::cout << *val << endl;
因此巧妙的使用if constexpr可避免模板特化,减少代码量。
参考文章:https://blog.csdn.net/janeqi1987/article/details/103542802
https://changkun.de/modern-cpp/zh-cn/02-usability/