constexpr

博主介绍:程序喵大人

关于编译期动作,有必要介绍下 constexpr。

在这之前有必要简单提一下 constexpr 与 const 的关系,两者字面上都表达常量的意思。主要的区别是:const 修饰的变量可以在运行时才初始化,而 constexpr 则一定会在编译期初始化。constexpr 才是名副其实的常量,所有的 constexpr 都是 const。而 const 表示的是 read only 的语义,保证修饰的变量运行时不可以更改,如果直接改动它,编译器在编译时会报错。const 修饰的变量可以在运行时才初始化,而 constexpr 则一定会在编译期初始化。

有人可能会用指针等骚操作来修改 const 修饰的变量值,这种情况下,CPP 标准规定产生的是未定义行为,具体可能不同编译器的具体行为会不相同。所以骚操作魔改 const 后,无论产生什么行为,都没必要奇怪,也没必要深究。

下面具体介绍下 constexpr。如上所述,constexpr 修饰的才是真正的常量,它会在编译期间计算出来,整个运行过程中都不可被改变。

constexpr 还可用于修饰函数,这个函数的返回值会尽可能在编译期间被计算出来,然后作为一个常量,但是如果编译期间不能被计算出,此函数就是被当作一个普通函数处理。

如何使用 constexpr?这里我直接贴出 cppreference 中的示例代码:

cpp 复制代码
#include <iostream>
#include <stdexcept>

// C++11 constexpr functions use recursion rather than iteration
// (C++14 constexpr functions may use local variables and loops)
constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

// literal class
class conststr {
    const char* p;
    std::size_t sz;
public:
    template <std::size_t N>
    constexpr conststr(const char (&a)[N]) : p(a), sz(N - 1) {}

    // constexpr functions signal errors by throwing exceptions
    // in C++11, they must do so from the conditional operator ?:
    constexpr char operator[](std::size_t n) const {
        return n < sz ? p[n] : throw std::out_of_range("");
    }

    constexpr std::size_t size() const { return sz; }
};

// C++11 constexpr functions had to put everything in a single return statement
// (C++14 doesn't have that requirement)
constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) {
    return n == s.size() ? c :
        'a' <= s[n] && s[n] <= 'z'
        ? countlower(s, n + 1, c + 1)
        : countlower(s, n + 1, c);
}

// output function that requires a compile-time constant, for testing
template <int n>
struct constN {
    constN() { std::cout << n << '\n'; }
};

int main() {
    std::cout << "4! = ";
    constN<factorial(4)> out1;  // computed at compile time

    volatile int k = 8; // disallow optimization using volatile
    std::cout << k << "! = " << factorial(k) << '\n';  // computed at run time

    std::cout << "the number of lowercase letters in \"Hello, world!\" is ";
    constN<countlower("Hello, world!")> out2;  // implicitly converted to conststr
}

可以大体观察到 constexpr 的语法如下:

复制代码
constexpr literal-type identifier = constant-expression ;
constexpr literal-type identifier { constant-expression } ;
constexpr literal-type identifier ( params ) ;
constexpr ctor ( params ) ;

通过示例代码及相关注释,就可以看到,能在编译期做 constexpr 就会优先在编译期计算,编译期不行就在运行时计算。

也可以看到,在 C++14 之前 constexpr 修饰函数时不能有 if-else、for 循环等语句,而在 C++14 后,这个问题有了改善。

那什么情况下应该使用 constexpr 修饰函数?不在乎编译时间的话,尽可能用 constexpr 修饰所有的函数,大家有时间可以看看 cpp 的源码,多数成员函数都是使用的 constexpr 修饰。

思考题:constexpr 有一个条件是需要满足 literal type,那 literal type 究竟是什么类型?

推荐阅读:
https://docs.microsoft.com/en-us/cpp/cpp/constexpr-cpp?view=msvc-170

参考资料
https://en.cppreference.com/w/cpp/language/constexpr

码字不易,欢迎大家点赞,关注,评论,谢谢!

相关推荐
沫璃染墨16 分钟前
从零入门计算机网络系列(十五):TCP可靠传输机制详解——从序号与确认应答到乱序处理
服务器·网络·c++·tcp/ip·tcp
青少儿编程课堂34 分钟前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:Bellman-Ford与SPFA含负权最短路及负环检测全解析
c++·python·算法·bfs·信息学竞赛
旖旎夜光1 小时前
【C++ 基础入门】类和对象(下):初始化列表、static 成员、友元与内部类
c++·笔记·学习
j7~1 小时前
【C++ 八股文】(篇四)C++11 新特性
开发语言·c++·面试·c++11·八股文
eggwyw1 小时前
C++中大括号的用法合集
开发语言·c++
xiaoye-duck1 小时前
《C++ 项目:手写大模型接入 SDK》本地 Ollama 部署与适配
开发语言·网络·c++·语言模型
联犀2 小时前
联犀 Go SIMD 实战:视频转码从 16.5ms 到 5.46ms
开发语言·golang·音视频
octopus_c2 小时前
STL 之 string:auto 与范围 for、常用接口、模拟实现与深浅拷贝
开发语言·c++·算法
Nerd(BUG版)2 小时前
KMP学习day5--Kotlin Flow异步流基础
开发语言·学习·kotlin
J.YA2 小时前
String字符串[面试题]
java·开发语言