温州网站建设 温州网站制作,软件网站建设,深圳歌剧院设计方案,北京公司摇号目录
一、constexpr 关键字
1.1 - constexpr 修饰普通变量
1.2 - constexpr 修饰函数
1.3 - constexpr 修饰类的构造函数
1.4 - constexpr 和 const 的区别
二、decltype 关键字
2.1 - 推导规则
2.2 - 实际应用 一、constexpr 关键字
constexpr 是 C11 新引入的关键字…目录
一、constexpr 关键字
1.1 - constexpr 修饰普通变量
1.2 - constexpr 修饰函数
1.3 - constexpr 修饰类的构造函数
1.4 - constexpr 和 const 的区别
二、decltype 关键字
2.1 - 推导规则
2.2 - 实际应用 一、constexpr 关键字
constexpr 是 C11 新引入的关键字不过在理解其具有用法和功能之前我们需要先理解 C 常量表达式。
所谓常量表达式指的是由多个 1常量组成的表达式换句话说如果表达式中的成员都是常量那么该表达式就是一个常量表达式这也意味着常量表达式一旦确定其值将无法修改。
实际开发中我们经常用到常量表达式以定义数组为例数组的长度就必须是一个常量表达式
int arr1[5] { 0, 1, 2, 3, 4 }; // ok
int arr2[2 * 5] { 0 }; // ok
// int len 10;
// int arr3[len] { 0 }; // error
我们知道C 程序从编写完毕到执行分为四个阶段预处理、编译、汇编和链接得到可执行程序后就可以运行了。值得一提的是常量表达式和非常量表达式的计算时机不同非常量表达式只能在程序运行阶段计算出结果而常量表达式的计算往往发生在程序的编译阶段这可以大大地提高程序的执行效率 因为表达式只需要在编译阶段计算一次节省了每次程序运行时都要计算一次的时间。
对于用 C 编写的程序性能往往是永恒的追求那么在实际开发中如何才能判断一个表达式是否为常量表达式进而获得在编译阶段即可执行的 特权 呢除了人为判定外还有我们一开始所提到的 C11 新引入的 constexpr 关键字 。
constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力而不必等到程序运行阶段。在 C11 中constexpr 可用于修饰普通变量、函数包括普通函数、类的成员函数以及模板函数以及类的构造函数。 注意获得在程序编译阶段计算出结果的能力并不代表 constexpr 修饰的表达式一定会在程序编译阶段被执行具体的计算时机还是编译器说了算。 1.1 - constexpr 修饰普通变量
C11 中定义普通变量时可以用 constexpr 修饰从而使该变量获得在编译阶段即可计算出结果的能力。
注意使用 constexpr 修饰普通变量时变量必须经过初始化且初始值必须是一个常量表达式。
constexpr int len 10;
int arr[len] { 0 }; // ok
在此示例中也可以将 constexpr 替换成 const即
const int len 10;
int arr[len] { 0 }; // ok 注意const 和 constexpr 并不相同关于它们的区别 后面会进行详解。 1.2 - constexpr 修饰函数
constexpr 还可以用于修饰函数的返回值这样的函数又称为 常量表达式函数。
注意constexpr 并非可以修饰任意函数的返回值换句话说一个函数要想成为常量表达式必须满足如下三个条件 函数必须有返回值即函数的返回值类型不能是 void。 constexpr void func() { } // 函数的返回值类型不能是 void 整个函数的函数体中除了可以包含 using 指令、typedef 语句以及 static_assert 断言以外只能包含一条 return 返回语句且 return 返回的表达式必须是常量表达式。 constexpr int func(int x)
{constexpr int y 0; // 函数体中只能包含一条 return 返回语句return 1 2 x y;
} int y 0;
constexpr int func(int x)
{return 1 2 x y; // return 返回的表达式必须是常量表达式
} #include iostream
using namespace std;
constexpr int y 0;
constexpr int func(int x)
{return 1 2 x;
}
int main()
{int arr[func(3)] { 0 };cout sizeof(arr) endl;return 0;
} 函数在使用之前必须有对应的定义语义。普通函数的调用只需要提前写好该函数的声明部分即可函数的定义部分可以放在调用位置之后甚至其他文件中但常量表达式函数在使用前必须要有该函数的定义。 #include iostream
using namespace std;
constexpr int func(int x);
int main()
{int arr[func(3)] { 0 };cout sizeof(arr) endl;return 0;
}
constexpr int func(int x)
{return 1 2 x;
} 以上三个条件不仅对普通函数适用对类的成员函数和模板函数也适用。 但由于函数模板中的类型不确定因此实例化后的模板函数是否符合常量表达式函数的要求也是不确定的针对这种情况C11 规定如果 constexpr 修饰的实例化后的模板函数不满足常量表达式函数的要求则 constexpr 会被自动忽略即该函数就等同于一个普通函数。 1.3 - constexpr 修饰类的构造函数
如果想直接得到一个常量对象也可以使用 constexpr 修饰一个构造函数这样就可以得到一个常量构造函数。常量构造函数有一个要求构造函数的函数体必须为空且必须采用初始化列表的方式为各个成员赋值。
#include iostream
using namespace std;
struct Person
{const char* _name;int _age;
constexpr Person(const char* name, int age): _name(name), _age(age){ }
};
int main()
{constexpr Person p{ 张三, 18 };cout p._name : p._age endl; // 张三:18return 0;
} 1.4 - constexpr 和 const 的区别
在 C11 之前只有 const 关键字其在实际使用中经常会表现出两种不同的语义。
void func(const int num)
{// int arr1[num] { 0 }; // errornum 是一个只读变量而不是常量const int count 5;int arr2[count] { 0 }; // okcount 是一个常量
} func 函数的参数 num 是一个只读变量其本质上仍然是变量而不是常量。 注意只读并不意味着不能被修改两者之间没有必然的联系例如 #include iostream
using namespace std;
int main()
{int a 520;const int ra a;a 1314;cout ra endl; // 1314return 0;
} 引用 ra 是只读的即无法通过自身去改变自己的值但并不意味着无法通过其他方式间接去改变通过改变 a 的值就可以改变 ra 的值。 func 函数体中的 count 则被看成是一个常量所以可以用来定义一个静态数组。 const int count 5;
int* ptr (int*)count;
*ptr 10;
cout count endl; 为什么输出的 count 和 *ptr 不同呢 具体原因是 C 中的常量折叠或者常量替换将 const 常量放在符号表中给其分配内存但实际读取时类似于宏替换。 为了解决 const 关键字的双重语义问题C11 引入了新的关键字 constexpr建议凡是表达 只读 语义的场景都使用 const凡是表达 常量 语义的场景都使用 constexpr。
所以在上面的例子中在 func 函数体中使用 const int count 5; 是不规范的应使用 constexpr int count 5;。 二、decltype 关键字
decltype 是 C11 新增的一个关键字它和 auto 一样都用来在编译期间进行自动类型推导。 decltype 是 declare type 的缩写即 声明类型。 既然有了 auto为什么还需要 decltype 呢因为 auto 并不适用于所有的自动类型推导场景在某些特殊情况下auto 用起来非常不方便甚至压根无法使用所以 decltype 被引入到 C11 中。
auto 和 decltype 的语法格式
auto varname value; // varname 表示变量名value 表示赋给变量的值
decltype(exp) varname[ value;] // exp 表示一个表达式
auto 根据 右边的初始值 value 推导出变量的类型所以使用 auto 声明的变量必须初始化而 decltype 根据 exp 表达式推导出变量的类型跟 右边的初始值 value 没有关系所以不要求初始化。
示例
#include iostream
using namespace std;
int main()
{int x 0;
decltype(x) y 1;decltype(x 3.14) z 5.5;decltype(x) ptr;
cout typeid(y).name() endl; // intcout typeid(z).name() endl; // doublecout typeid(ptr).name() endl; // int *
// 注意// decltype 的推导是在编译期间完成的// 它只是用于表达式类型的推导并不会计算表达式的值decltype(x) i;cout x endl; // 0return 0;
} 2.1 - 推导规则
当程序员使用 decltype(exp) 获取类型时编译器将根据以下三条规则得出结果 如果表达式为普通变量、普通表达式或者类成员访问表达式那么 decltype(exp) 的类型就和表达式的类型一致。 #include iostream
using namespace std;
class Test
{
public:string _str;static int _i;
};
int Test::_i 0;
int main()
{int x 0;int r x;decltype(x) y x; // y 被推导为 int 类型decltype(r) z x; // z 被推导为 int 类型z;cout x r y z endl; // 1 1 0 1
Test t;decltype(t._str) s hello world; // s 被推导为 string 类型decltype(Test::_i) j 10; // j 被推导为 int 类型return 0;
} 如果表达式是函数调用那么 decltype(exp) 的类型和函数返回值一致。 #include iostream
using namespace std;
// 函数声明
int func_int();
int func_int_r();
const int func_c_int();
const int func_c_int_r();
int main()
{int x 0;
decltype(func_int()) y x; // y 被推导为 int 类型decltype(func_int_r()) z x; // z 被推导为 int 类型z;cout x y z endl; // 1 0 1
decltype(func_c_int()) m x; // m 被推导为 int 类型m;cout x y z m endl; // 1 0 1 2
decltype(func_c_int_r()) n x; // n 被推导为 const int 类型return 0;
} 注意函数 func_c_int() 的返回值是一个纯右值即在表达式执行结束后不再存在的数据也就是临时性的数据对于纯右值而言只有类类型可以携带 const、volatile 限定符除此之外需要忽略这两个限定符因此 m 被推导为 int 类型而不是 const int 类型。 如果表达式是一个左值、或者被括号 () 包围那么 decltype(exp) 的类型就是表达式类型的引用即假设 exp 的类型为 T那么 decltype(exp) 的类型就是 T。 #include iostream
using namespace std;
int main()
{int x 0;decltype((x)) y x; // y 被推导为 inty;cout x y endl; // 1 1
decltype(x x 1) z x; // z 被推导为 intz;cout x y z endl; // 2 2 2return 0;
} 2.2 - 实际应用
decltype 的应用多出现在泛型编程中。
#include vector
using namespace std;
templateclass T
class Test
{
public:void func(T container){_it container.begin();// do something ... ...}
private:decltype(T().begin()) _it;// 当 T 是普通容器_it 为 T::iterator// 当 T 是 const 容器_it 为 T::const_iterator。
};
int main()
{vectorint v; Testvectorint t1;t1.func(v);
const vectorint v2;Testconst vectorint t2;t2.func(v2);return 0;
}