为什么要使用constexpr ?
Why use constexpr
让我们以一个简单的SFINAE模板为例
#include <iostream>
template <typename T>
struct has_typedef_foobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::foobar*);
template <typename>
static no& test(...);
// If the "sizeof" of the result of calling test<T>(0) would be equal to sizeof(yes),
// the first overload worked and T has a nested type named foobar.
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
struct foo {
typedef float foobar;
};
int main() {
std::cout << std::boolalpha;
std::cout << has_typedef_foobar<int>::value << std::endl;
std::cout << has_typedef_foobar<foo>::value << std::endl;
}
bool
值没有被声明为constexpr
,但它仍然在编译时获得其值。那么constexpr
的使用是什么,为什么简单的静态变量在编译时得到它们的值?我如何知道哪些静态变量在编译时得到它们的值?
也我怎么能告诉哪些值将得到评估在编译时,哪些不会?使用constexpr
能保证编译时的评估吗?如果不是,我怎么知道会发生什么(编译时或运行时)?
constexpr
为您提供了以前不可能实现的关键功能之一,即在需要编译时常量的上下文中调用函数的能力。最简单的例子是数组大小:
#include <iostream>
using namespace std;
constexpr auto square(int x) { return x * x; }
int main()
{
int arr[square(2)] = { 0, 1, 2, 3 };
cout << arr[square(1)] << endl;
}
没有constexpr
的square()
函数,它不能在arr
的定义中调用,因为数组大小需要是编译时常数。
虽然您可以编写一个模板,使您的编译时间square
先于constexpr
,但您不能将该模板与非编译时间常数参数一起使用,因此您将最终获得编译时间和非编译时间版本的代码重复。对于大多数程序员来说,模板的语法也比简单的函数定义更复杂,更不熟悉。
事实上,constexpr
很少保证编译器在编译时何时会选择求值。在需要值为编译时常数(例如数组大小)的上下文中,它当然是。在其他情况下,这很大程度上取决于编译器-在访问arr[square(1)]
时调用square()
,例如,编译器可以在运行时自由评估,尽管在实践中,我希望大多数编译器在编译时评估这个,至少在优化的构建中。
这不是一个好问题,因为constexpr
是一个巨大的功能,它有很多"点"。
最主要的一点是,你可以使用(更)普通的c++语法而不是模板元编程语言来进行编译时计算。
例如,如果您曾经尝试在编译时使用模板进行字符串操作,那么您可能会将这种体验与这里描述的一些技术进行对比:
constexpr基本上是一种新的编译时元编程语言,它与模板语言并行存在,像constexpr构造函数这样的东西允许你在编译时实例化结构体,这在模板中是不可能的。
另一个重要的"点"是,您不必为编译时和运行时编写不同的代码。标记为constexpr 的代码可以在编译时运行,也可以在运行时轻松运行。模板代码…必须完全在编译时运行,并且通常看起来与等效的运行时代码非常不同。因此,在某些情况下,使用constexpr代码更DRY。
- 为什么constexpr的性能比正常表达式差
- 为什么std::isnan 不是 constexpr?
- 为什么我不能在 constexpr lambda 函数中使用 std::tuple
- 为什么reinterpret_cast不是 constexpr?
- 为什么我的 constexpr 对象在我的函数中不是 constexpr?
- 为什么这两段使用 constexpr、__PRETTY_FUNCTION__ 和 char * 的代码有不同的结果?
- 为什么从 constexpr 引用生成的程序集代码与从 constexpr 指针生成的程序集代码不同?
- 如果 constexpr语言 - 为什么完全检查丢弃的语句?
- 为什么带有指针子对象的文字类类型的 constexpr 表达式不能是非类型模板参数
- 为什么必须将 const 添加到 constexpr 中才能进行字符串文字声明?
- 为什么 std::launder 是一个 constexpr 函数?
- 为什么我不能声明一个 constexpr 本地,而一个 const 一个工作?
- 为什么 std::get<T> 其中 T 是调用 constexpr 函数失败的结果?
- 为什么 std::array 的运算符 ==() 没有标记为 constexpr?
- 为什么非成员静态 constexpr 变量不是隐式内联的?
- 如果有 constexpr if 语句,为什么没有其他 constexpr 语句呢?
- 为什么递归 constexpr 模板值无法编译?
- 从语言设计层面来看,当编译时无法推断条件时,为什么"if constexpr"不衰减到"trival if"
- 为什么编译器无法弄清楚构造函数实际上是 constexpr?
- 为什么 clang 并不总是为相同的静态 constexpr 产生常量值