常量表达式和constexpr(c++11)

时间:2021-07-25 23:34:01

常量表达式

  常量表达式是指值不会改变且在编译阶段就能得到计算结果的表达式(两点要求)

const int max_files = ;    //是常量表达式
const int limit = max_file + ; //是常量表达式
string s="";
const int siz=s.size(); //不是常量表达式
int staff_size = ; //不是常量表达式
const int sz = get_size(); //不是常量表达式,因为尽管sz本身是一个常量,但是它的值直到程序运行时才能获得

constexpr(c++11)

  c++11允许将变量声明为constexpr类型让编译器来验证变量是否为一个常量表达式

constexpr int mf = ;
constexpr int limit = mf + ;
constexpr int sz = get_size(); //只有当get_size()是一个constexpr函数时才是正确的声明

const变量 和 constexpr 变量之间的主要区别

1.const 变量的初始化可以延迟到运行时,而 constexpr 变量必须在编译时进行初始化:

const int sz = get_size();    //正确的声明,但是sz不是常量表达式
constexpr int sz = get_size(); //只有当get_size()是一个constexpr函数时才是正确的声明

2.constexpr声明中如果定义了一个指针,限定符constexpr只对指针有效,与指针所指的对象无关:

const int* p = nullptr;    //p是一个指向整型常量的指针
constexpr int* q = nullptr; //q是一个指向整型的常量指针
constexpr const int* r = nullptr; //r是一个指向整型常量的常量指针

constexpr函数

  用限定符constexpr修饰函数,使之用于常量表达式中,constexpr函数有这么几个特性

1.函数的返回类型和所有的形参类型都是字面值类型

2.函数体中只有一条语句,而且只能是一条return语句(但其实也是可以包含其他语句的,只要这些语句在程序运行时不执行任何操作就行,如:空语句、类型别名、using声明)

constexpr int new_sz()
{
return ;
}

3.编译器其实也允许constexpr函数的返回值和形参不是字面值(编译器有时可忽略函数的constexpr属性)

constexpr int get_size(int a)
{
return a;
} int main()
{
int size = ;
int y = get_size(size); //正确,编译器此时忽略函数的constexpr属性,因为y是非constexpr的,可以不要求get_size是constexpr的
constexpr int z = get_size(size); //错误!!! int arr1[get_size()]; //正确
int arr2[get_size(size)]; //错误,数组的大小要求是常量表达式 return ;
}

4.constexpr函数自带inline属性

5.inline函数和constexpr函数可以在程序中多次定义,一般定义在头文件中,但是多个定义必须完全一致

现在假设我们有2个cpp文件和一个.h头文件:main.cpp(main函数在这个文件里), haha.cpp, xixi.h

main.cpp:

void fuck(){}

int main()
{
return ;
}

haha.cpp:

void fuck(){}

如果只编译main.cpp的话,是不会有重复定义的错误的。但是如果同时编译main.cpp和haha.cpp的话,那就会有重复定义错误。IDE一般都是会默认编译所有文件。

好,那能不能只定义在.h头文件里,然后另外两个cpp文件单纯include?

main.cpp:

#include "xixi.h"

int main()
{
return ;
}

haha.cpp:

#include "xixi.h"

xixi.h:

void fuck(){}

结果是也不行,因为include其实就是把xixi.h里面的东西拷到cpp里,结果跟上面那种是一样的,也是在link的时候报错。

好,那我又想了,不是有个什么#ifndef的东西吗?给我的xixi.h加上这个行不行,这样不就只有一个fuck函数的定义了吗?

#ifndef XIXI
#define XIXI
void fuck(){}
#endif

结果还是不行,依然报重复定义错误,所谓的防止头文件重复包含是对应一个cpp文件。也就是说,这个#ifndef可以保证在haha.cpp里,#ifndef里面的内容是唯一的,也能保证在main.cpp里是唯一的,但不能保证你在这个程序里(同时用到main.cpp和haha.cpp)是唯一的。

考虑下面这个例子:

main.cpp:

#include "xixi.h"

int main()
{
return ;
}

haha.cpp:

#include "xixi.h"

xixi.h:

#ifndef XIXI
#define XIXI
void fuck(); //这是声明
#endif

xixi.cpp:

#include"xixi.h"

void fuck(){}//定义

这样将不会有重复定义的错误。因为include的头文件里面没有函数定义,只有声明,而同一个函数是可以多次声明。

再回到inline函数的展开,展开需要什么,需要函数体,所以inline函数要定义在头文件里。但最开始的几个错误例子说了,定义在头文件的函数定义被多个cpp文件include后会有重复定义的错误,怎么办呢?用inline关键字可以避免这个问题,指明inline,就不会有这个错误:

main.cpp:

#include "xixi.h"

int main()
{
return ;
}

haha.cpp:

#include "xixi.h"

xixi.h:

inline void fuck(){}

这样也不会有重复定义的错误。

综上,inline函数有2个作用:

  1. 函数展开,避免函数调用开销,(用空间换时间)
  2. 定义在头文件的inline函数被多个cpp文件include不会产生重复定义(在程序可以被多次定义的意思不是说可以在文件里重复定义,其实是给inline函数做一个标记,假如有10个inline函数体,然后最后会删除剩下1份)