constexpr
,inline
和static
可能是C++里最让人迷惑的几个关键词了,如同vector<bool>
既不是一个vector
,里面也不存放bool
一样。
当出现组合static constexpr
,static inline
等这种组合的时候,更是让人摸不着头脑。
constexpr和const的区别
简单来说,const
的含义是readonly variable
,constexpr
代表的是真的constant
,用法如同纯C里的#define A 100
一样。
如果你想要真正的常量,那就请使用constexpr
。
考虑如下代码
#code 1
volatile constexpr int a = 5;
int *p = (int *)&a;
*p = 100;
printf("a = %d, *p = %d\n", a, *p);
#code 2
volatile const int a = 5;
int *p = (int *)&a;
*p = 100;
printf("a = %d, *p = %d\n", a, *p);
gcc-8
编译,结果会是
a = 5, p = 100
a = 100 p = 100
constexpr
也可以用来修饰函数,表示函数会在编译器被计算,然而constexpr
并不一定保证函数会在编译期被计算。这个设计是为了
避免仅仅因为编译期计算和运行期计算的细微差别的函数重载。但是这个特性可能会对导致一点编译错误,幸运的是这点错误会在编译期就被编译器拒绝接受
如下
考虑以下代码
#include <array>
constexpr int foo(int i)
{
return i + 1;
}
int main()
{
int i = 5;
std::array<int,6> bar1;
std::array<int,foo(5)> bar2;
// std::array<int,foo(i)> bar3; // error
#define N 6
std::array<int,N> bar4;
return 0;
}
foo(i)
传入了一个非const
变量,所以它的constexpr
标识符失效,从而导致std::array<>
不能编译。
static
static
的毛病在于,它可以用用的范围太广以至于它在修饰不同的东西的时候,带有不同的含义。
它主要有几个用处
-
当
static
用于面向对象中,即类里面修饰变量和成员函数时,它代表所有对象都共用同一个成员函数和静态数据成员。从这个含义上来讲static
和inline
有相似之处。 在C++11
里加入的constexpr
加强了static
对静态数据成员的功能,现在可以在类内直接初始化constexpr static
成员了。 -
当
static
被用于全局变量的时候,又分为两种情况。如果是在cpp
文件内声明一个static
全局变量,那么该变量只在该cpp文件内可见,其他cpp无法访问该变量。 当在头文件里定义static
全局变量时,每一个包含了该头文件的cpp
都会维持一份该全局变量的拷贝,这里和inline
完全相反。如果你在头文件里实现了某个函数, 并标注为static
,那么每一个包含该头文件的cpp
都会复制一份该函数。 -
当
static
用于函数内部的局部变量时,表示延长该局部变量的生命周期,延长到程序结束为止,但是访问域限制在函数以内,只有函数内部可以修改该变量。
inline
inline
的问题在于,它的含义在c++
的发展历程中,发生了转变。早期的inline
表示建议编译器在这里进行优化,现在编译器基本都会忽略inline
,自己选择优化。
现在inline
的意思已经变成了:在头文件中修饰的变量和函数(包含实现)的时候,每一个引用头文件的cpp
文件所持有的该函数/变量,都是同一个函数/变量。
inline
现在最大的用处在,如果想要非模板函数在头文件内实现和定义不分离,则必须加上inline
,这样在多个引用该函数的cpp
文件内,持有的函数都是同一个函数。
static inline
这种把人往死里坑的声明,实际上就等于static
,inline
会被忽略掉。
下面上一段简单的测试代码
// static.h
#pragma once
inline int i = 5; //result 1
//static int i = 5; //result 2
//static inline i = 5; //result 3 和static相同
// static_test1.h
#pragma once
#include "static.h"
void print_i();
// static_test2.h
#pragma once
#include "static.h"
void print2_i();
// static_test1.cpp
#include <iostream>
#include "static_test1.h"
void print_i()
{
std::cout<< &i <<"i="<<i<<std::endl;
}
// static_test2.cpp
#include <iostream>
#include "static_test2.h"
void print2_i()
{
std::cout<< &i <<"i="<<i<<std::endl;
}
// main.cpp
#include <iostream>
#include "static_test1.h"
#include "static_test2.h"
int main()
{
print_i();
print2_i();
}
用std=gnu++17
编译如下(inline variable
是cpp17
的规范)
//result
0x55f7d853a070i=5
0x55f7d853a070i=5
//result 2
0x563bd8c61070i=5
0x563bd8c61074i=5
//result 3
0x56270bb4f070i=5
0x56270bb4f074i=5