C++ constexpr用法详解(附带实例)
在编译时运算表达式可以改善执行时间,因为需要执行的代码少了,编译器可做额外的优化。
编译时常量不仅仅是字面量(如数字或字符串),也可以是函数执行的结果。如果函数的所有输入值(不管它们是参数、局部变量或全局变量)在编译时已知,则编译器可以执行该函数并可在编译时获取其值。
C++ 中的关键字 constexpr(constant expression 的简称)用于声明编译时常量对象和函数。
1) 定义可在编译时运算的非成员函数:
2) 定义可在编译时执行的用来初始化 constexpr 对象的构造函数及编译时调用的成员函数:
3) 定义可在编译时被运算的变量:
然而,运算常量表达式仍可能需要运行时计算。constexpr关键字用来声明编译时为常量的变量或可在编译时运行的函数。constexpr 函数和对象可替换宏和硬编码字面值,而不会有性能损失。
函数声明为 constexpr 并不意味着它通常在编译时进行运算。它只允许在编译时运算的表达式中使用该函数。这只有当函数的输入值均可在编译时运算时才会发生。
以下代码展示了同一函数的两次调用,第一次是在编译时,第二次是在运行时:
constexpr 的使用范围有一些限制。在 C++14 和 C++20 中,这些限制有所变化。为了保持合理的列表,这里只展示 C++20 中需要满足的要求:
1) constexpr 的变量必须满足以下要求:
2) constexpr的函数必须满足以下要求:
3) 除了前面函数的要求外,constexpr 的构造函数必须额外满足以下要求:
4) 除了之前函数的要求外,C++20 之后的 constexpr 析构函数,还必须额外满足以下要求:
constexpr 类型的函数不是隐式的 const(在 C++14 中),因此如果函数不修改对象逻辑状态,那么你需要显式地使用 const 说明符。然而,constexpr 类型的函数是隐式的 inline(内联)。
另外,声明为 constexpr 的对象隐式地为 const。以下两个声明是等价的:
一些情况下你需要同时声明 constexpr 和 const,它们可能是声明的不同部分。在以下示例中,p 是 constexpr 指针,指向常量整型:
引用变量也可以是 constexpr,当且仅当它们是静态存储周期对象或函数的别名。以下代码片段给出了一个示例:
编译时常量不仅仅是字面量(如数字或字符串),也可以是函数执行的结果。如果函数的所有输入值(不管它们是参数、局部变量或全局变量)在编译时已知,则编译器可以执行该函数并可在编译时获取其值。
C++ 中的关键字 constexpr(constant expression 的简称)用于声明编译时常量对象和函数。
C++ constexpr的使用方式
使用 constexpr 关键字,当你想要:1) 定义可在编译时运算的非成员函数:
constexpr unsigned int factorial(unsigned int const n) { return n > 1 ? n * factorial(n-1) : 1; }
2) 定义可在编译时执行的用来初始化 constexpr 对象的构造函数及编译时调用的成员函数:
class point3d { double const x_; double const y_; double const z_; public: constexpr point3d(double const x = 0, double const y = 0, double const z = 0) : x_{x}, y_{y}, z_{z} {} constexpr double get_x() const {return x_;} constexpr double get_y() const {return y_;} constexpr double get_z() const {return z_;} };
3) 定义可在编译时被运算的变量:
constexpr unsigned int size = factorial(6); char buffer[size] {0}; constexpr point3d p {0, 1, 2}; constexpr auto x = p.get_x();
深度剖析C++ constexpr
C++ 中,const 关键字用来声明变量运行时为常量,这意味着一旦初始化,它们不可被更改。然而,运算常量表达式仍可能需要运行时计算。constexpr关键字用来声明编译时为常量的变量或可在编译时运行的函数。constexpr 函数和对象可替换宏和硬编码字面值,而不会有性能损失。
函数声明为 constexpr 并不意味着它通常在编译时进行运算。它只允许在编译时运算的表达式中使用该函数。这只有当函数的输入值均可在编译时运算时才会发生。
以下代码展示了同一函数的两次调用,第一次是在编译时,第二次是在运行时:
constexpr unsigned int size = factorial(6); // compile time evaluation int n; std::cin >> n; auto result = factorial(n); // runtime evaluation
constexpr 的使用范围有一些限制。在 C++14 和 C++20 中,这些限制有所变化。为了保持合理的列表,这里只展示 C++20 中需要满足的要求:
1) constexpr 的变量必须满足以下要求:
- 它的类型是字面类型;
- 它在声明的时候进行初始化;
- 用来初始化变量的表达式是常量表达式;
- 它必须有常量析构函数。这意味着它不是类类型或类类型的数组;否则,类类型必须有 constexpr 析构函数。
2) constexpr的函数必须满足以下要求:
- 它不是协程;
- 返回类型和它所有参数的类型都是字面类型;
- 至少有一组参数,可使函数产生常量表达式;
- 函数体必须不包含 goto 语句、标签(switch 里的 case 和 default 除外),以及非字面类型、静态或线程存储周期的局部变量。
3) 除了前面函数的要求外,constexpr 的构造函数必须额外满足以下要求:
- 类没有虚基类;
- 初始化非静态数据成员(包括基类)的所有构造函数必须是 constexpr。
4) 除了之前函数的要求外,C++20 之后的 constexpr 析构函数,还必须额外满足以下要求:
- 类没有虚基类;
- 析构非静态数据成员(包括基类)的所有析构函数必须是 constexpr。
constexpr 类型的函数不是隐式的 const(在 C++14 中),因此如果函数不修改对象逻辑状态,那么你需要显式地使用 const 说明符。然而,constexpr 类型的函数是隐式的 inline(内联)。
另外,声明为 constexpr 的对象隐式地为 const。以下两个声明是等价的:
constexpr const unsigned int size = factorial(6); constexpr unsigned int size = factorial(6);
一些情况下你需要同时声明 constexpr 和 const,它们可能是声明的不同部分。在以下示例中,p 是 constexpr 指针,指向常量整型:
static constexpr int c = 42; constexpr int const * p = &c;
引用变量也可以是 constexpr,当且仅当它们是静态存储周期对象或函数的别名。以下代码片段给出了一个示例:
static constexpr int const & r = c;在此示例中,r 是一个 constexpr 引用,定义了编译时常量对象 c 的别名,c 则在前面的代码片段中定义。