constexpr (C++)

关键字 constexpr 是在 C++11 中引入的,并在 C++14 中进行了改进。 它表示 constant(常数)表达式。 与 const 一样,它可以应用于变量:如果任何代码试图 modify(修改)该值,将引发编译器错误。 与 const 不同,constexpr 也可以应用于函数和类 constructor(构造函数)。 constexpr 指示值或返回值是 constant(常数),如果可能,将在编译时进行计算。

每当需要 const 整数时(如在模板自变量和数组声明中),都可以使用 constexpr 整数值。 如果在编译时(而非运行时)计算某个值,它可以使程序运行速度更快、占用内存更少。

为了限制计算编译时 constant(常数)的复杂性及其对编译时间的潜在影响,C++14 标准要求 constant(常数)表达式中所涉及的类型为文本类型

语法

constexprliteral-typeidentifier=constant-expression;
constexprliteral-typeidentifier{constant-expression};
constexprliteral-typeidentifier(params);
constexprctor(params);

参数

params
一个或多个参数,每个参数都必须是文本类型,并且本身必须是 constant(常数)表达式。

返回值

constexpr 变量或函数必须返回文本类型

constexpr 变量

constconstexpr 变量之间的主要 difference(区别)是,const 变量的初始化可以推迟到运行时进行。 constexpr 变量必须在编译时进行初始化。 所有的 constexpr 变量都是 const

  • 如果一个变量具有文本类型并且已初始化,则可以使用 constexpr 声明该变量。 如果初始化是由 constructor(构造函数)performed(执行)的,则必须将 constructor(构造函数)声明为 constexpr

  • 当满足以下两个条件时,引用可以被声明为 constexpr:引用的对象是由 constant(常数)常数表达式初始化的,初始化期间调用的任何隐式转换也是 constant(常数)表达式。

  • constexpr 变量或函数的所有声明都必须具有 constexpr specifier(说明符)。

constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression

constexpr 函数

constexpr 函数是在使用需要它的代码时,可在编译时计算其返回值的函数。 使用代码需要编译时的返回值来初始化 constexpr 变量,或者用于提供非类型模板自变量。 当其自变量为 constexpr 值时,函数 constexpr 将生成编译时 constant(常数)。 使用非 constexpr 自变量调用时,或者编译时不需要其值时,它将与正则函数一样,在运行时生成一个值。 (此双重行为使你无需编写同一函数的 constexpr 和非 constexpr 版本。)

constexpr 函数或 constructor(构造函数)通过隐式方式 inline

以下规则适用于 constexpr 函数:

  • constexpr 函数必须只接受并返回文本类型

  • constexpr 函数可以是递归的。

  • 可以是fore C++20,函数constexpr不能是虚拟的,当封闭类具有任何虚拟基类时,不能定义const一个 constexprructor。 在 C++20 及更高版本中,constexpr 函数可以是虚函数。 指定 /std:c++20 或更高版本的编译器选项时,Visual Studio 2019 版本 16.10 及更高版本支持 constexpr 虚函数。

  • 主体可以定义为 = default= delete

  • 正文不能包含如何 goto 语句或 try 块。

  • 可以将非 constexpr 模板的显式专用化声明为 constexpr

  • constexpr 模板的显式专用化不需要同时也是 constexpr

以下规则适用于 Visual Studio 2017 及更高版本中的 constexpr 函数:

  • 它可以包含 ifswitch 语句,以及所有循环语句,包括 for、基于范围的 forwhile、和 do-while

  • 它可能包含局部变量声明,但必须初始化该变量。 它必须是文本类型,不能是 static 或线程本地的。 本地声明的变量不需要是 const,并且可以变化。

  • constexprstatic 成员函数不需要通过隐式方式 const

constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

提示

在 Visual Studio 调试器中,在调试非优化调试版本时,可以看出 constexpr 函数是否是通过在其内部放置一个断点在编译时计算的。 如果命中该断点,则在运行时调用该函数。 如果未命中,则在编译时调用该函数。

extern constexpr

/Zc:externConstexpr 编译器选项使编译器将外部链接应用于使用 extern constexpr声明的变量。 在早期版本的 Visual Studio 中,默认情况下或者 specified(指定)了 /Zc:externConstexpr- 时,即使使用关键字 extern,Visual Studio 也会将内部链接应用于 constexpr 变量。 从 Visual Studio 2017 Update 15.6 开始,可以使用 /Zc:externConstexpr 选项,默认情况下它处于关闭状态。 /permissive- 选项不会启用 /Zc:externConstexpr

示例

下面的示例展示了 constexpr 变量、函数和用户定义类型。 在 main() 的最后一个语句中,constexpr 成员函数 GetValue() 是一个运行时调用,因为不需要在编译时知道该值。

// constexpr.cpp
// Compile with: cl /EHsc /W4 constexpr.cpp
#include <iostream>

using namespace std;

// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1) / 2) * x;
}

// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
    return N;
}

// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }
private:
    int _i;
};

int main()
{
    // foo is const:
    constexpr Foo foo(5);
    // foo = Foo(6); //Error!

    // Compile time:
    constexpr float x = exp(5, 3);
    constexpr float y { exp(2, 5) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1, 2, 3, 4 };
    const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };

    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
}

要求

Visual Studio 2015 或更高版本。

另请参阅

声明和定义
const