嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > 一文了解 constexpr

一文了解 constexpr

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


在这之前有必要简单提一下constexpr与const的关系,两者字面上都表达常量的意思。


主要的区别是:const修饰的变量可以在运行时才初始化,而constexpr则一定会在编译期初始化。


constexpr才是名符其实的常量,所有的constexpr都是const。


而const表示的是read only的语义,保证修饰的变量运行时不可以更改,如果直接改动它,编译器在编译时会报错。const修饰的变量可以在运行时才初始化,而constexpr则一定会在编译期初始化。


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



下面具体介绍下constexpr。


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


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


如何使用constexpr?


这里我直接贴出cppreference中的示例代码:

#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



- EOF -


加主页君微信,不仅C/C++技能+1

主页君日常还会在个人微信分享C/C++开发学习资源技术文章精选,不定期分享一些有意思的活动岗位内推以及如何用技术做业余项目

加个微信,打开一扇窗


推荐阅读  点击标题可跳转

1、

2、

3、


关注『CPP开发者』

看精选C/C++技术文章

点赞和在看就是最大的支持❤️