Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何使用constexpr #56

Open
nine-point-eight-p opened this issue Jun 13, 2022 · 0 comments
Open

如何使用constexpr #56

nine-point-eight-p opened this issue Jun 13, 2022 · 0 comments

Comments

@nine-point-eight-p
Copy link

constexpr的使用

[constexpr和const的区别](https://github.com/thu-coai/THUOOP/issues/56)为大家介绍了什么是`constexpr`。简而言之,`constexpr`告知编译器这是一个编译期常量表达式(const expression),可以在编译时计算变量或函数的值,从而可以在只允许编译期常量表达式的地方使用。这里我们继续拓展下去,来简单介绍如何在C++11下使用constexpr

使用constexpr修饰的限制

使用constexpr修饰变量或函数有一定的条件,因为这样才能保证可以在编译期计算出来。不过应当注意,这些限制其实在随着C++标准不断变化,以下只是C++11下的约定。

一个constexpr变量必须满足以下条件:

  • 其类型必须是字面类型(Literal Type)。字面类型包括数值、引用、满足特定条件的自定义类,以及由字面类型组成的数组。严格的定义参见参考资料。不太严谨地说,字面类型就是能够在编译器计算得到,从而可以被称之为constexpr的类型(但是这样就会循环定义了)。

  • 必须立即初始化。

  • 其初始化的完整表达式,包括所有隐式转换、构造函数调用等,必须是一个常量表达式。

一个constexpr函数(包括类的构造函数、成员函数在内的所有函数)必须满足以下条件:

  • 不是虚函数。

  • 若有返回值或参数,则必须是字面类型。

  • 函数体要么是default或delete,要么只能包含以下内容:

    • 空语句(即单个分号;);
    • static_assert(静态断言,可以看作是编译期版本的assert);
    • 不定义类或枚举的类型的typedef(反例:typedef struct {} A;);
    • 声明类的别名的using(using declarations);
    • 使用某个命名空间的using(using directives);
    • 只能调用其它constexpr函数;
    • 只能使用全局constexpr变量;
    • 最多只有一条return语句。
  • 对于构造函数,还有额外的要求:要么是delete,要么用于初始化每个非静态数据成员以及基类的函数都必须是constexpr

可见constexpr的要求还是比较严格的。

什么时候使用constexpr

显然,constexpr使我们可以在编译期进行一些工作,这和C中的宏具有类似的功能。例如在编译期获得比较大小的值:

template<typename T>
constexpr T max(T a, T b)
{
    return a < b ? b : a;
}

或者我们可以换算角度和弧度:

constexpr double PI = 3.1415926;
constexpr double deg_to_rad(double deg)
{
    return deg * PI / 180.0;
}

甚至我们可以在编译期递归计算阶乘(三目运算符是可以出现在函数体中的):

constexpr int factorial(int n)
{
	return n > 0 ? n * factorial(n - 1) : 1;
}

但是我们到底为什么要用constexpr呢?在编译期得到结果固然重要,但constexpr还表明了一种设计意图:我们在写这段代码时认为这个变量或函数应该在编译期得到。这个声明不仅是告知编译器,更是要告知接触到这段代码的人。作为使用者,假设说我们用上面阶乘函数的结果去初始化一个数组,如int arr[factorial(0)];。如果没有constexpr,显然不能用来初始化数组;如果有constexpr,那么你才能放心地初始化数组。作为维护者,constexpr标记表明了它预设的使用场景,因此你也应该选择合适的方法予以实现和优化。所以说constexpr是对这一段代码究竟应在何时使用的保证。

参考资料

  1. [c++ - When should you use constexpr capability in C++11? - Stack Overflow](https://stackoverflow.com/questions/4748083/when-should-you-use-constexpr-capability-in-c11)
  2. [C++ named requirements: LiteralType - cppreference.com](https://en.cppreference.com/w/cpp/named_req/LiteralType)
  3. [constexpr specifier (since C++11) - cppreference.com](https://en.cppreference.com/w/cpp/language/constexpr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant