c++11新特性篇-constexpr

constexpr

constexpr 是C++11引入的新特性。constexpr 是常量表达式(Constant Expression)的缩写,用于在编译时计算表达式的值。通过将变量或函数声明为 constexpr,可以指示编译器在编译时求值,而不是在运行时进行计算。

1. const

const 是C++中的一个关键字,用于声明常量或指示某些对象在程序执行过程中不可修改。const 可以用于不同的上下文,包括变量、指针、引用、成员函数等。

以下是 const 在不同上下文中的用法:

1.1 常量变量
const int x = 42;  // 常量整数,不可修改
1.2 常量指针
const int* ptr;  // 指向常量整数的指针,通过指针不能修改所指向的值
int const* ptr;  // 与上一行等效,指向常量整数的指针
int* const ptr;  // 常量指针,指针本身不可修改
1.3 常量引用
const int& ref = x;  // 常量引用,通过引用不能修改所引用的值
1.4 成员函数中的const
class MyClass {
public:
    void normalFunction() {
        // 可以修改对象的成员变量
    }

    void constFunction() const {
        // 不可以修改对象的成员变量
    }
};

1.5 常量对象
class MyClass {
public:
    int x;
    void modify() {
        x = 42;
    }
};

const MyClass obj;  // 常量对象,不能通过该对象调用 modify 函数

const 的主要作用是提高代码的可读性和安全性,通过限制对变量的修改,可以防止一些意外的改变。在程序设计中,使用 const 可以帮助更好地表达代码的意图,并在一定程度上提高代码的可维护性。

2. 变量只读和常量的区别

在C++11之前只有const关键字,从功能上来说这个关键字有双重语义:变量只读,修饰常量,举一个简单的例子:

void func(const int num)
{
    const int count = 24;
    int array[num];            // error,num是一个只读变量,不是常量
    int array1[count];         // ok,count是一个常量

    int a1 = 520;
    int a2 = 250;
    const int& b = a1;
    b = a2;                         // error
    a1 = 1314;
    cout << "b: " << b << endl;     // 输出结果为1314
}
  • 函数void func(const int num)的参数num表示这个变量是只读的,但不是常量,因此使用int array[num]; 这种方式定义一个数组,编译器是会报错的,提示num不可用作为常量来使用。
  • const int count = 24;中的count却是一个常量,因此可以使用这个常量来定义一个静态数组。

因此,变量只读并不等价于常量,二者是两个概念不能混为一谈,分析一下这句测试代码const int& b = a1;

  • b是一个常量的引用,所以b引用的变量是不能被修改的,也就是说b = a2; 这句代码语法是错误的。
  • 在const对于变量a1是没有任何约束的,a1的值变了b的值也就变了
  • 引用b是只读的,但是并不能保证它的值是不可改变的,也就是说它不是常量。

3.constexpr

在C++11中添加了一个新的关键字constexpr,这个关键字是用来修饰常量表达式的。所谓常量表达式,指的就是由多个(≥1)常量(值不会改变)组成并且在编译过程中就得到计算结果的表达式。

C++ 程序从编写完毕到执行分为四个阶段:预处理、 编译、汇编和链接4个阶段,得到可执行程序之后就可以运行了。需要额外强调的是,常量表达式和非常量表达式的计算时机不同,非常量表达式只能在程序运行阶段计算出结果,但是常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率,因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。

那么问题来了,编译器如何识别表达式是不是常量表达式呢?在C++11中添加了constexpr关键字之后就可以在程序中使用它来修饰常量表达式,用来提高程序的执行效率。在使用中建议将 const 和 constexpr 的功能区分开,即

凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。

在定义常量时,const 和 constexpr 是等价的,都可以在程序的编译阶段计算出结果,例如:

const int m = f();  // 不是常量表达式,m的值只有在运行时才会获取。
const int i=520;    // 是一个常量表达式
const int j=i+1;    // 是一个常量表达式

constexpr int i=520;    // 是一个常量表达式
constexpr int j=i+1;    // 是一个常量表达式

对于 C++ 内置类型的数据,可以直接用 constexpr 修饰,但如果是自定义的数据类型(用 struct 或者 class 实现),直接用 constexpr 修饰是不行的。

// 此处的constexpr修饰是无效的
constexpr struct Test
{
    int id;
    int num;
};

如果要定义一个结构体/类常量对象,可以这样写:

struct Test
{
    int id;
    int num;
};

int main()
{
    constexpr Test t{ 1, 2 };
    constexpr int id = t.id;
    constexpr int num = t.num;
    // error,不能修改常量
    t.num += 100;
    cout << "id: " << id << ", num: " << num << endl;

    return 0;
}

代码中t.num += 100;的操作是错误的,对象t是一个常量,因此它的成员也是常量,常量是不能被修改的。

4.常量表达式函数

4.1使用概述

常量表达式函数(constexpr functions)是指在编译时就能够被计算的函数。在C++11引入了 constexpr 关键字后,程序员可以声明函数为常量表达式函数,以便在编译时执行,而不是在运行时。这提供了一些性能优势,因为编译时计算可以避免在运行时进行一些重复的计算。

constexpr 函数有以下一些特点:

  1. 在编译时执行: constexpr 函数的参数和返回值必须是可以在编译时计算的类型,这通常包括整数、指针等。在调用 constexpr 函数时,如果传入的参数是常量表达式,那么函数的执行将在编译时完成,结果会被直接嵌入到生成的机器代码中。
  2. 限制: 在C++11中,constexpr 函数的功能受到一些限制,例如只能包含单一的 return 语句、不能包含循环等。C++14引入了更多的灵活性,允许 constexpr 函数包含更复杂的控制流结构,条件语句和循环。

以下是一个简单的示例:

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int result = square(5);  // 在编译时计算结果
    // ...
    return 0;
}

在这个例子中,square 函数被声明为 constexpr,并在 main 函数中用于计算常量表达式 square(5)。由于参数是常量表达式,函数调用在编译时就能够被计算,因此 result 在编译时就得到了值。

4.2扩展

常量表达式函数可以修饰普通函数, 模板函数, 构造函数

普通函数如上所述, 下面讲一下修饰模板函数和构造函数

修饰模板函数

C++11 语法中,constexpr 可以修饰函数模板,但由于模板中类型的不确定性,因此函数模板实例化后的模板函数是否符合常量表达式函数的要求也是不确定的。如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。

修饰构造函数

如果想用直接得到一个常量对象,也可以使用constexpr修饰一个构造函数,这样就可以得到一个常量构造函数了。常量构造函数有一个要求:构造函数的函数体必须为空,并且必须采用初始化列表的方式为各个成员赋值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值