为什么要使用 constexpr

Why use constexpr

来个简单的SFINAE templating example

#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 值尚未声明 [​​=12=] 但它仍会在编译时获取其值。那么 constexpr 的用途是什么?为什么简单的静态变量在编译时获取它们的值?我如何知道哪些静态变量在编译时获得它们的值?

另外,我如何判断哪些值会在编译时求值,哪些不会?使用 constexpr 会保证编译时评估吗?如果不是,我怎么知道会发生什么(编译时间或 运行 时间)?

这不是一个很好的问题,因为 constexpr 是一个巨大的功能,它有很多 "points"。

最主要的是,与模板元编程语言相比,您可以使用(更多)普通 C++ 语法进行编译时计算。

例如,如果您曾尝试在编译时使用模板进行字符串操作,您可能会将这种体验与此处描述的一些技术进行对比:Conveniently Declaring Compile-Time Strings in C++

constexpr 基本上是一种新的编译时元编程语言,它与模板语言并行存在,并且像 constexpr 构造函数这样的东西允许您在编译时实例化结构,这对于仅使用模板是不可能的。

另一大 "point" 是您不必为编译时和 运行 时编写不同的代码。标记为 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;
}

如果函数 square() 上没有 constexpr,则无法在 arr 的定义中调用它,因为数组大小需要作为编译时常量。

虽然您可以编写一个模板,让您的编译时间 square 先于 constexpr,但您不能将该模板与非编译时间常量参数一起使用,因此您最终会得到编译时和非编译时版本的代码重复。对于大多数程序员来说,模板的语法比简单的函数定义更复杂,也更不熟悉。

事实上,constexpr 很少保证编译器何时会实际选择在编译时评估某些内容。在要求值是编译时常量(例如数组大小)的情况下,它当然是。在其他情况下,这在很大程度上取决于编译器——例如,在访问 arr[square(1)] 时调用 square(),编译器可以在运行时自由评估,尽管实际上我希望大多数编译器在编译时评估它时间,至少在优化构建中。