Wang's blog

const与constexpr

Published on

const

C++11之前就存在const关键字,用于进行常量相关操作。它的主要用法如下:

// 定义常量,使其不能修改
const int value = 5;

// 可以使用常量定义数组
char arr[value];

char s[10];
// 指针不可修改,指向的内容可修改
char *const ptr1 = s;
// 指针可修改,指向的内容不可修改
const char *ptr2;
// 指针与指向的内容均不可修改
const char *const ptr3 = "string";

// 修饰函数参数,使其不可修改,如果同时使用引用可避免复制对象
void func(const A &a);

// 修饰成员变量,使其不能修改,只能进行初始化
class A
{
    const int value = 5;
};
class B
{
    const int value;
    B(int v) : value(v) {}
};

// 修饰成员函数,使其不能修改成员变量
class C
{
public:
    void func() const {}
};
// 修饰对象,使其只能调用const成员函数
const C c;
c.func();

constexpr

C++11引入关键字constexpr用于定义常量表达式,常量表达式的值在编译期间即可被计算出来。constexpr不仅可修饰变量,也可修饰函数与构造函数/析构函数等。

constexpr变量

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

// 正确
constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);

// 错误,未初始化
constexpr int i;
// 错误,j不是常量表达式
int j = 0;
constexpr int k = j + 1;

constexpr函数

在需要时,constexpr函数的返回值可在编译期间计算,并可用来初始化其它constexpr变量。如果不需要在编译期间计算,则constexpr函数与普通函数一样,在运行期间执行。

// 可在constexpr函数中使用递归
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

自定义类型的字面值

// 实现具有constexpr构造函数的自定义类型
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }

private:
    int _i;
};

// 声明自定义类型的字面值
constexpr Foo foo(5);

constexpr if

从C++17开始,可以使用if constexpr语句,这将根据其后的条件在编译期间确定一条分支,并舍弃另一条分支。

template <typename T>
auto get_value(T t)
{
    if constexpr (std::is_pointer_v<T>)
        return *t;
    else
        return t;
}