constexpr是C++的修饰词,用于指定"编译期可以求数值的常量表达式",可用于修饰变量,函数,类/结构体,构造函数/析构函数,模板五种情况,作用就是支持编译期常量处理。constexpr的实际应用中最常用的是修饰变量和函数。
constexpr用于修饰变量,可以构建真常量,即编译器常量。constexpr用于修饰函数,可以用于编译期求值;constexpr用于修饰类/结构体。可以用来在编译期构造对象;constexpr用于修饰构造函数/析构函数,可以在编译器就对类对象进行初始化/销毁;constexpr用于修饰模板,在编译期间完成模板实例化进行求数值。
增加编译期间求解的效果是提升性能、增强类型安全。
实例1------constexpr修饰变量:
C++
#include <iostream>
constexpr int MAXsize = 100; // 由constexptr修饰的int型真常量,
//在编译期就可以通过直接赋值确定取值为100.
constexpr int OVERsize = MAXsize + 50;
//在编译期可以通过计算得到确定取值为150.
//由constexpr修饰的MAXsize和OVERsize两个真常量,分别取值100和150.
//编译正常。符合处理直觉。
提问: constexpr 可以修饰变量,此时该变量是编译期常量(值必须在编译期确定,且生命周期内不可修改,比 const 更严格)如果这个变量的取值在编译期间没有确定会报错吗?还是任意给这个变量取定一个随机值?取定随机值会导致这个变量实际没有可用效果? 以及这里讲的生命周期不可修改是什么意思?一个constexpr修饰的真常量的生命周期不就是整个程序运行的期间都有效吗?编译期常量即真常量是不占用C++堆内存和栈内存的吧!
arduino
解答:
// constexpr 修饰变量,这个变量的数值必须在编译期确定,生命周期内不可修改。
// 如果编译期间这个变量的数值没有确定指定,会直接编译报错,无随机值。
// constexpr 修饰得到的真常量在生命周期内都只具有只读属性,不可修改。
// constexpr 修饰得到的真常量的生命周期取决于变量定义所在的作用域决定。
// 即constexpr只负责跟编译器通力合作在编译期间确定数值。
// 至于这个真常量在何时失效取决于这个变量的自身的作用域,
// 即定义在函数内部的变量,即便加上constexpr修饰,
// 这个真常量的生命周期依然只在函数内的局部作用域效果下。constexpr修饰的真常量被存储在静态只读数据区。
总结1:
constexpr修饰变量,得到真常量,存储在静态存储区,不占用运行内存;(因此增强性能)constexpr修饰得到的真常量,如果没有在编译期指定,直接报错。constexpr修饰得到的真常量,生命周期内权限为只读,不可修改。constexpr修饰得到的真常量,生命周期长度取决于变量本身的生命周期。
实例2------constexpr修饰函数:
表示这个函数,如果入参是编译期常量,在编译期完成计算得到处理结果。如果入参不是编译期间常量,那就退化成普通函数。两种情况都不会报错。UDL和普通函数都支持这种退化兼容。
C++
//constexpr修饰UDL(自定义字面值运算符)
constexpr long long operator "" _KM2m(long long kilometer) {
return kilometer*1000;
}
//constexpr修饰普通函数
constexpr int add(int a, int b){
return a + b;
}
实例3------constexpr修饰类/结构体:
作用是在编译期构造类对象,即编译期常量对象。 要求:
- 类的构造函数必须是
constexpr构造函数; - 编译期内需调用的成员函数也必须有
constexpr修饰; - 类的成员变量中,对于被
constexpr使用/依赖的静态成员变量,必须在类内constexpr修饰。而且最好直接用static constexpr
C++
//constexpr修饰结构体(在C++中将结构体视为类的一种)
struct Point{
//constexpr 修饰构造函数
constexpr Point(int x_, int y_) : x(x_), y(y_) {}
// C++中约定俗成用 变量名右加下划线 表示类成员变量的命名。
// x(x_), y(y_) 是 C++ 中的「成员初始化列表(Member Initialization List)
// 效果是将x_,y_的值对应赋值给x,y
// `x(x_)` 和 `x=x_` 在初始化列表中等价
//constexpr 修饰构造函数
constexpr int get_x() const {
return}
}
int x;
int y;
}
int main () {
//编译器构造Ponit对象
constexpr Point p0(10,20);
//编译期调用成员函数,获取数值
constexpr int x_val = p0.get_x();
return 0;
}
- 非静态成员变量是指属于单个对象的变量,每个对象独一份,生命周期随对象变化;
- 静态成员变量属于类本身的,所有对象共享一份,生命周期贯穿程序。
- 静态const成员变量:类内直接初始化,因为贯穿程序的生命周期,且在运行期间数值不可修改,是绑定在整个类上的变量。
static const变量类型 变量名 = 初始化数值;static constexpr变量类型 变量名 = 初始化数值; 效果更好。
- -这里的static其实是指类共享。静态变量本质是类共享变量;非静态变量是类不共享变量。
- 静态const成员变量:类内直接初始化,因为贯穿程序的生命周期,且在运行期间数值不可修改,是绑定在整个类上的变量。
实例4------constexpr修饰构造函数/析构函数:
构造函数用constexpr修饰,要么非空要么只涉及基本四则运算和判断的简洁函数体。 析构函数用constexpr修饰,得到的效果是编译期对象销毁时没有运行期开销。 (constexpr修饰的效果要么是用于设定真常量,要么是能在编译期间能处理的事情给处理了。)
C++
class MyInt{
public:
//构造函数
constexpr MyInt (int val_) : val(val_) {}
//析构函数
constexpr ~MyInt() = default; //析构函数直接 = default 就行。
//成员函数
constexpr int get_val() const {
return val;
}
private:
int val;
};
int main() {
//在编译期构造 MyInt 对象
constexpr MyInt mi(100);
constexpr int val = mi.get_val(); // 编译期求值为100
return 0;
}
关联说明类成员函数的const尾缀修饰: constexpr int get_val() const { return val; //这个const成员函数只读取,没有修改val的权限。 }
const成员函数
作用是限定这个成员函数对成员变量的操作权限仅仅是只读。
- 禁止修改类的非静态成员变量,确保这个函数只进行只读操作,不会改变对象的状态。
- 禁止调用非
const成员函数。
const 是 "承诺不修改成员变量",仅支持运行期执行;constexpr 是 "支持编译期求值",
实例5------constexpr修饰模板template:
使模板实例化后具备编译器求值能力。
C++
template <template T>
constexpr T max_val(T a, T b){
return a > b ? a : b;
}
int main() {
constexpr int max_num = max_val(5,10);
//编译期求值: max_val(5,10)实例化得到int型数据,取值为10.
return 0;
}