C++11开始constexpr作为一种声明,为编译器提供了在编译期间确认结果的优化建议,满足部分编译期特性的需求
constexpr和const区别
int b=10;
const int a=b;
//运行成功
constexpr int c=b;
//编译器报错,b的值在编译期间不能确定
const int size1(){return 100;}
constexpr int size2(){return 100;}
char buf1[size1()];//报错
char buf2[size2()];//通过
constexpr修饰的变量它的值必须在编译阶段可以确认,const只是作为常类型。
对constexpr修饰的函数的约束(C++11)
- 函数必须有一个返回值即返回值类型不能是void
- 函数体内只能有一行用于输出返回值且返回值必须是常量表达式
- 函数必须定义且必须用constexpr声明
注意:即使定义了一个常量表达式函数,如果形参不是常量表达式的话函数会退化为普通函数
字面类型(常量表达式中所有类型必须是字面类型)
- 构造函数用constexpr声明
- 构造函数初始化参数列表里是常量表达式
- 构造函数的函数体为空
- 基本数据类型、指针、引用、各种字面量都是字面类型
C++ 14标准规则:
- 函数体允许声明初始化过的变量,除了static和thread_local变量
- constexpr不在具有const属性
- 开始支持部分代码如:if,switch,各种循环
- 可以修改字面类型数据
- 返回值可以是void了
C++17开始lamada表达式可以用constexpr声明时会进行隐式声明
char *buf[[](){return 1024;};];
C++17 提出了if constexpr,它可以根据一个常量表达式的返回结果有选择的编译部分代码,可以简化模板的编写,比如:特化的实现
template <typename T>
void printSize(T value) {if constexpr (std::is_integral<T>::value) {std::cout << "Size of integral type: " << sizeof(T) << " bytes" << std::endl;} else if constexpr (std::is_floating_point<T>::value) {std::cout << "Size of floating-point type: " << sizeof(T) << " bytes" << std::endl;} else {std::cout << "Size of other type: " << sizeof(T) << " bytes" << std::endl;}
}
C++20 标准
- 支持constexpr虚函数
- 运行在constexpr中进行一些默认初始化
- 支持try catch和typeid,dynamic_cast等运算符的使用
constexpr相关关键字
- consteval关键字,强制编译期出结果,得不到结果程序结束(C++ 20)
- constinit关键字,用于具有静态存储持续时间的(生命周期全局)变量声明上,他要求变量具有常量初始化程序(编译阶段初始化),某种程度上可以提高程序效率
应用:
封装static_assert的参数,STL中sort源码中快排部分判断比较器是否有效时,将比较对象的顺序调换看比较结果是否是否不同,所以使用sort时比较器必须是常量表达式函数
STL中sort的源码中插入排序部分就用到了常量表达式用于判断迭代器的种类,如果是线性迭代器使用memmove直接移动数据,否则就需要使用循环一个一个的移动对象,这里不详细的说明了,有时间的化会写一篇博客讲一下sort源码