不那么新的modern C++简要学习笔记 logo 不那么新的modern C++简要学习笔记

编译器支持最低版本要求:

  • GCC: 4.6
  • MSVC: 19.0
  • Clang: 3.1

提案: N2235

背景

在C++11之前,程序中的常量表达式主要依赖于宏定义和const变量。但这些方式都存在一些局限性:

为了解决这些问题,C++11引入了constexpr关键字,用于声明可以在编译期计算的常量表达式。

基本用法

constexpr变量

constexpr变量必须用常量表达式初始化:

constexpr int size = 42; // OK
constexpr int double_size = size 2; // OK
constexpr int random = rand(); // 错误:rand()不是常量表达式

constexpr函数

constexpr函数在编译期可计算,但也可以在运行时使用:

constexpr int square(int x) {
    return x x;
}
// 编译期计算
constexpr int sq_5 = square(5); // sq_5 = 25,编译期确定
// 运行时计算
int some_value = get_value();
int sq_val = square(some_value); // 运行时计算

使用场景

1. 数组维度声明

constexpr int get_array_size() { return 42; }
int array[get_array_size()]; // OK,编译期可确定大小

2. 模板参数

template<int N>
struct Array {
    int data[N];
};
constexpr int size = 10;
Array<square(size)> arr; // OK,square(size)在编译期计算

3. 编译期计算提升性能

constexpr int fibonacci(int n) {
    return (n <= 1) ? n : fibonacci(n-1) + fibonacci(n-2);
}
// 编译期就计算出结果
constexpr int fib_10 = fibonacci(10);

限制条件

C++11中的constexpr函数有较多限制:

注意:这些限制在C++14中得到了很大放松。

最佳实践

  1. 当需要编译期常量时,优先使用constexpr而不是宏定义
  2. 设计编译期可计算的函数时,尽量使用constexpr声明
  3. constexpr函数应保持简单,避免复杂的逻辑
  4. 记住constexpr函数可以在运行时使用,这提供了很好的灵活性

与其他特性的对比

constexpr vs const

  1. 计算时机
    • const:仅表示运行时常量,不可修改
    • constexpr:表示编译期常量,编译器会在编译期计算其值
  2. 使用场景
// const示例
const int val1 = 42; // OK
const int val2 = get_value(); // OK,运行时初始化
int arr1[val1]; // 错误:数组大小需要编译期常量
int arr2[val2]; // 错误:数组大小需要编译期常量
// constexpr示例
constexpr int val3 = 42; // OK
constexpr int val4 = get_value(); // 错误:需要编译期常量表达式
int arr3[val3]; // OK,val3是编译期常量
  1. 函数声明
// const成员函数:表示不会修改对象状态
class MyClass {
    int value;
    public:
    int getValue() const { return value; } // 运行时执行
};
// constexpr函数:可在编译期执行
constexpr int square(int x) { return x x; } // 编译期执行
  1. 主要区别总结:
    • const主要用于:
      • 声明运行时不可修改的变量
      • 声明不修改对象状态的成员函数
      • 声明指向常量的指针或引用

constexpr vs #define

  1. 类型安全性
#define MAX_SIZE 100 // 无类型检查
constexpr int MaxSize = 100; // 有类型检查
void foo(short s) {
    #define SQUARE(x) ((x) (x))
    short s1 = SQUARE(s); // 可能溢出,无警告
    constexpr auto square = {
        return x x;
    };
    short s2 = square(s); // 编译器会进行类型检查
}
  1. 作用域
    • #define:无作用域概念,全局生效
    • constexpr:遵循正常的作用域规则
  2. 调试友好性
    • #define:预处理阶段处理,调试时看不到原始值
    • constexpr:是正常的变量和函数,调试时可以看到具体值
  3. 主要优势对比:

使用建议

  1. 优先级顺序:
    • 需要编译期常量时:首选constexpr
    • 仅需要运行时常量时:使用const
    • 仅在确实需要预处理器功能时:使用#define
  2. 场景选择:
    • 模板参数:使用constexpr
    • 数组大小:使用constexpr
    • 运行时只读变量:使用const
    • 条件编译:使用#define

总结

constexpr是C++11引入的重要特性,它为编译期计算提供了类型安全和更好的可维护性。通过合理使用constexpr,我们可以:

随着C++14、C++17的发展,constexpr的功能更加强大,使用场景也更加广泛。

感觉不错,小额赞助一下!
missdeer WeChat Pay

微信扫一扫

missdeer AliPay

支付宝扫一扫

Hosted by Netlify, 本站由 @missdeer 创建,由 Jekyll 于 2024-11-07 生成,感谢 CloudCannon 制作的theme: Edition ,感谢 Let's Encrypt 提供免费的SSL证书服务。本站点采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。© 2017 - 2024