C++11 新特性:常量表达式 constexpr(上)

C++11 引入了constexpr关键字,用于定义常量表达式,从而使变量获得在编译阶段即可计算出结果的能力,提高运行时的效率。

constexpr 的使用分两篇文章介绍,今天这篇文章主要讲解什么是常量表达式和 constexpr 典型使用场景的前三种用法。constexpr 在模板编程中的使用,在下篇文章中进行讲述。

什么是常量表达式

常量表达式(Constant Expression)指的是值在编译时就已经确定,并且在程序执行过程中不会改变的表达式。

在 C++ 中,常量表达式可以用于定义编译时常量、数组的大小、整型模板参数、编译时断言等场合,是提高程序效率、实现编译时计算的重要工具。

我们知道,C++ 在定义数组的时候,必须指定数组长度,长度不能用变量指定。

cpp 复制代码
int myArray[10]; // 正确
int myArray[6 + 4]; // 正确
int len = 6;
int myArray[len]; // 错误,len 是变量

这里的 len 可以使用常量表达式替代。

常量表达式的特点

  • 编译时计算:保证变量或函数在编译时被求值,前提是所有参数或初始值也都是常量表达式。
  • 不变性:一旦编译确定,其值在程序运行期间不会改变。
  • 提高性能:通过在编译时进行计算,减少了运行时的计算负担。
  • 类型安全:与预处理器宏相比,常量表达式是类型安全的。

常量表达式的用途

  • 定义常量:提供编译时的常量定义,如数组大小、枚举值等。
  • 模板编程:作为模板参数,尤其是非类型模板参数,提供编译时的灵活性和强大功能。
  • 编译时断言:配合 static_assert 使用,进行编译时的条件检查。

constexpr 是对 C++ 中常量表达式概念的扩展,它可以用于变量、函数以及构造函数等。除了具有上述常量表达式的特点,constexpr 的使用范围更广。

constexpr 典型使用场景

  • 定义常量:用于定义编译时常量。
  • 编译时函数计算:定义能在编译时求值的函数。
  • 用于类和构造函数:在编译时创建对象。
  • 用于模板编程:在模板元编程和编译时断言中使用,根据编译时计算的结果做出决策。

1、定义常量

cpp 复制代码
const int maxSize = 100; // 基本的常量表达式
constexpr int limit = maxSize + 1; // 编译时常量表达式

constexpr size_t arraySize = 10;
int myArray[arraySize]; // 使用常量表达式作为数组大小

2、编译时函数计算

constexpr 还可以用于修饰函数的返回值,这样的函数又称为「常量表达式函数」。

注意,constexpr 函数体内的所有操作都必须是编译时确定的。一个函数要想成为常量表达式函数,必须满足如下 4 个条件。

  1. 整个函数的函数体中,除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句。

  2. 该函数必须有返回值,即函数的返回值类型不能是 void。

  3. 函数在使用之前,必须有对应的定义语句。普通的函数调用只需要提前写好该函数的声明部分即可,但常量表达式函数在使用前,必须要有该函数的定义。

  4. return 返回的表达式必须是常量表达式

cpp 复制代码
constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main() {
    constexpr int val = factorial(5); // 在编译时计算5!
    static_assert(val == 120, "Factorial of 5 should be 120."); // 使用编译时断言验证结果
    return 0;
}

3、用于类和构造函数

constexpr 也可以用于构造函数,使得对象可以在编译时被创建。

注意constexpr 修饰类的构造函数时,要求该构造函数的函数体必须为空,采用初始化列表的方式为各个成员赋值,且必须使用常量表达式。

前面提到,constexpr 可用于修饰函数,而类中的成员方法完全可以看做是「位于类这个命名空间中的函数」,所以 constexpr 也可以修饰类中的成员函数,只不过此函数必须满足前面提到的 4 个条件。

示例代码如下:

cpp 复制代码
class Point {
public:
    constexpr Point(double xVal, double yVal) : x(xVal), y(yVal) {}

    constexpr double getX() const { return x; }
    constexpr double getY() const { return y; }

private:
    double x, y;
};

int main() {
    constexpr Point p(9.5, 7.3); // 编译时创建Point对象
    static_assert(p.getX() == 9.5, "X should be 9.5."); // 编译时断言
    return 0;
}

总结

constexpr 使得编译时计算和常量定义更加灵活和强大,有助于提高 C++ 程序的性能和类型安全。

相关推荐
Yongqiang Cheng2 分钟前
Lambda expressions in C++ (C++ 中的 lambda 表达式)
c++·lambda 表达式
TANGLONG22217 分钟前
【C++】揭开C++类与对象的神秘面纱(首卷)(类的基础操作详解、实例化艺术及this指针的深究)
java·开发语言·数据结构·c++·python·考研·面试
GGBondlctrl18 分钟前
【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述
java·后端·代理模式·spring aop·切点表达式
是阿建吖!18 分钟前
【C++】C++11(二)
c语言·开发语言·c++
代码驿站52022 分钟前
Scala语言的软件开发工具
开发语言·后端·golang
wlyang66627 分钟前
2. Scala 高阶语法之集合与元组
开发语言·后端·scala
nSponge32 分钟前
【MFC】设置CTreeCtrl单个节点的文字颜色
c++·mfc
喜欢猪猪32 分钟前
大厂架构之极致缓存策略实战与原理剖析
java·后端·spring
天上的猩猩Y33 分钟前
VS调试MFC进入系统源代码配置
c++·mfc
社会零时工37 分钟前
【MFC练习入门篇——数青蛙】
c++·mfc