一. const 和constexpr的区别

(一)修饰变量时,const为“运行期常量”,即运行期数据是只读的。而constexpr为“编译期”常量,这是const无法保证的。两者都是对象和函数接口的组成部分。

(二)修饰函数时,与const关键字相比,constexpr关键字不仅可以修饰变量和指针,还可以修饰函数(含构造函数)。注意constexpr用于定义自定义类的对象时,要求该类具有常量构造函数,而使用const定义类对象时,则无此要求。

(三)两者在修饰指针时,行为有所差异。const放在*号前,表示指针指向的内容不能被修改.const放在*号后,表示指针不能被修改;而constexpr关键字只能放在*号前面,并且表示指针所指的内容不能被修改

二、常量表达式函数

(一)构成constexpr函数的条件

  1. 函数体只有单一的return语句(可以通过“:”或递归来扩展)。在C++14中这条己经不再是限制。

  2. 函数体必须有返回值(C++11中要求不能是void函数,但C++14己不再限制)

  3、constexpr函数内部不能调用非常量表达式的函数,会造成编译失败。

  4. return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式。

(二)注意事项

  1. 在使用常量表达式函数前,必须先被定义。不能先声明,然后在函数调用后再定义。

  2. constexpr函数在调用时若传入的实参均为编译期己知的,则返回编译期常量。只要任何一个实参在编译期未知,则它的运作与普通函数无异,将产生运行期结果

  3. constexpr函数的构成条件不满足时,就会变成一个普通的函数。同时constexpr函数可以同时应用于编译期语境或运行期语境(编译期语境如constexpr int a = func_constexpr(x, y)。运行期语境如int a = func_constexpr(x, y))。

【编程实验】const和constexpr的差异

#include <iostream>
#include <array> using namespace std; int g_count = ; int normalfunc(int x)
{
return x;
} //constexpr函数(即可当编译期常量表达式函数,也可以当作普通函数使用)
constexpr int func1(int x)
{
return x + ;
} constexpr void func2(){} //普通函数,constexpr必须有非void的返回值类型
constexpr int func3(); //注意这里只声明,定义在main()函数之后
//constexpr int func4() //编译失败
//{
// int x = normalfunc(2); //调用了非constexpr函数!
// return x;
//} //幂函数:用于计算base的exp次幂
constexpr int pow(int base, int exp)
{
//版本1:递归(一条语句)----> C++11要求有且只有一条语句(即return语句)。
//return (exp == 0) ? 1 : base * pow(base, exp - 1); //版本2:非递归(多条语句) --->C++14以上
auto ret = ;
for (int i = ; i < exp; ++i) {
ret *= base;
} return ret;
} int main()
{
//1.1 修饰变量时
int x = ; //非constexpr变量
const int y = ;
constexpr int a = ; //a为编译期常量
constexpr int b = a + ; //b为编译期常量
constexpr int c = y + ; //c为编译期常量,y直接取自符号表,而不是来自内存值。 //constexpr auto m = x; //error,因为x不是常量
const auto n = x; //ok, d为x的副本 constexpr auto N = ;
//std::array<int, n> arr1; //error, n不是编译期常量
std::array<int, N> arr2; //ok, N为编译期常量
std::array<int, c> arr3; //ok, c为编译期常量 constexpr auto exp = ;
std::array<int, pow(, exp)> arr4; //ok! pow是constexpr函数
//std::array<int, pow(3, x)> arr5; //error! x为运行期变量,从而导致pow返回值为运行期的值 //1.2 constexpr修饰指针时,表示指针本身不可修改!!!(constexpr只能放在*的左侧)
constexpr int* ptr2 = &g_count; //ok,全局变量,编译期地址可确定
//ptr2 = nullptr; //注意,与const不同,这里表示ptr2是常量。
*ptr2 = ; //constexpr int* ptr1 = &x; //error,因为x在栈上分配,在编译期不能确定其地址。 //1.3 修饰函数时
int var1 = func3(); //ok! func3先声明,使用后定义。
//constexpr int var2 = func3(); //error,在调用函数前,func3必须定义好了(不能只看到声明)。 constexpr int c1 = func1(N); //constexpr语境: 返回值赋值constexpr变量,是constexpr函数(实参也为constexpr)。
int c2 = func1(x); //非constexpr语境:func1用于返回值赋值给普通变量时,当作普通函数使用!
int c3 = func1(N); //非constexpr语境:func1用于返回值赋值给普通变量时,当作普通函数使用! return ;
} constexpr int func3() //声明放在main之前,这里是定义!
{
return ;
}

三、constexpr的应用

(一)定义自定义类的constexpr对象。

  1. 自定义类的构造函数须为constexpr函数

  2. constexpr不能用于修饰virtual函数。因为virtual是运行时的行为,与constexpr的意义冲突。

  3. C++11中被声明为constexpr成员函数会自动设为const函数,但C++14中不再自动设为const函数。

(二)函数模板: 实例化后的函数是否为constexpr函数,在编译期是不确定的,取决于传入的实参是否为constexpr类型

(三)constexpr类型的函数递归

  1. 利用constexpr进行编译期运算的编程方式称为constexpr元编程

  2 .基于模板编译期运算的编程方式,叫模板元编程。

【编程实验】constexpr的应用

#include <iostream>
using namespace std; //1. constexpr构造函数和成员函数
class Point
{
double x, y;
public:
//常量表达式构造函数
constexpr Point(double x=, double y=) noexcept : x(x),y(y){} //成员函数(注意:constexpr不允许用于修饰virtual函数)
constexpr double getX() const noexcept { return x;} //constexpr函数,在C++11中默认为const函数。但C++14中取消了,这里加const
constexpr double getY() const noexcept { return y;} //以下两个函数在C++11中无法声明为constexpr(C++14可以!),原因如下:
//1.C++11中被声明为constexpr成员函数会自动设为const函数,而这两个函数需要更改成员变量的值,这与const成员函数
//却不允许需改成员的值,会产生编译错误。C++14中constexpr函数不再默认为const函数,因此可以修改成员变量。
//2. C++11中constexpr函数不能返回void型,但C++14中不再限制。
constexpr void setX(double newX) noexcept { x = newX; } //C++11中须去掉constexpr;
constexpr void setY(double newY) noexcept { y = newY; }
}; //计算中点坐标
constexpr Point midpoint(const Point& p1, const Point& p2) noexcept
{
//p1和p2均为const对象,会调用const版本的getX()和getY()
return { (p1.getX() + p2.getX()) / , (p1.getY() + p2.getY()) / };
} //返回p相对于原点的中心对称点(C++14)
constexpr Point reflection(const Point& p) noexcept
{
Point ret;
ret.setX(-p.getX());
ret.setY(-p.getY());
return ret;
} //2. 函数模板:(constexpr元编程)
//函数是否为constexpr函数,编译期未知。取决于传入的实参t是否为constexpr变量/对象
template<typename T>
constexpr T func(T t)
{
return t;
} struct NotLiteral
{
int i;
NotLiteral():i(){} //注意:构造函数不是constexpr类型!
}; //3.constexpr类型的递归函数
//3.1 求斐波那契数
constexpr int Fib(int n)
{
return (n == ) ? : ((n == ) ? : Fib(n-) + Fib(n -));
} //3.2 模板递归(模板元编程)
template<long N>
struct Fibonacci
{
static const long val = Fibonacci<N - >::val + Fibonacci<N - >::val;
}; //特化
template<> struct Fibonacci<> { static const long val = ; };
template<> struct Fibonacci<> { static const long val = ; };
template<> struct Fibonacci<> { static const long val = ; }; void printArray(int a[], int len)
{
cout << "Fibonacci: ";
for (int i=; i<len; ++i)
{
cout << a[i] << " ";
}
cout << endl;
} int main()
{
//1. 用constexpr定义自定义类的对象(要求该类具有constexpr构造函数)
constexpr Point p1(9.4, 27.7); //ok, p1为编译期常量
constexpr Point p2{ 28.8, 5.3 }; //ok,同上
//1.1求中点
constexpr auto mid = midpoint(p1, p2); //mid为编译期常量,mid.getX()也是编译期常量!!!
//1.2求关于原点的对称点
constexpr auto reflectedMid = reflection(mid); //2. constexpr型的函数模板
NotLiteral n1; //非constexpr对象
NotLiteral n2 = func(n1); //传入实参为非constexpr对象,func成为普通函数!
//constexpr NotLiteral n3 = func(n1); //error,由于NotLiteral的构造函数不是constexpr类型,
//不能用constexpr定义该类型的constexpr的对象!!!
constexpr int a = func(); //ok,1为constexpr对象 //3. constexpr类型递归函数
int fib1[]{ Fib(), Fib(), Fib(), Fib() };
printArray(fib1, ); int fib2[] = { Fibonacci<>::val, Fibonacci<>::val, Fibonacci<>::val, Fibonacci<>::val };
printArray(fib1, ); return ;
}
/*输出结果
Fibonacci: 89 144 233 377
Fibonacci: 89 144 233 377
*/

第8课 常量表达式(constexpr)的更多相关文章

  1. 常量表达式 & constexpr

    [常量表达式] 一个这样的表达式:值不会改变 && 在编译过程中就能够得到计算结果 常见的常量表达式:字面值.用常量表达式初始化的const对象 一个对象是不是常量表达式由它的数据类型 ...

  2. constexpr和常量表达式

    常量表达式:值不会改变并且在编译过程就能得到计算结果的表达式. 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式. 一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同 ...

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

    常量表达式 常量表达式是指值不会改变且在编译阶段就能得到计算结果的表达式(两点要求) ; //是常量表达式 ; //是常量表达式 "; const int siz=s.size(); //不 ...

  4. const限定符、constexpr和常量表达式------c++ primer

    编译器将在编译过程中把用到const变量的地方都替换成对应的值,为了执行这种替换,编译器必须知道变量的初始值.如果程序包含多个文件,则那个用了const对象的文件都必须能访问到它的初始值才行.要做到这 ...

  5. 10、初识constexpr和常量表达式

    常量表达式:是指值不会改变并且在编译过程就能得到计算结果的表达式.显然字面值属于常量表达式,用于表达式初始化的const对象也是常量表达式. 1.判断一个变量是不是常量表达式 一个对象(表达式)是不是 ...

  6. C++常量表达式、const、constexpr(C++11新增)的区别

    常量表达式是指值不会改变且在编译过程中就能够得到计算结果的表达式,能在编译时求值的表达式. 程序先编译再运行:  在编译阶段, 编译器将在编译过程中把用到该常量的地方都全都替换为 常量的值. 但是常量 ...

  7. c++nullptr(空指针常量)、constexpr(常量表达式)

    总述     又来更新了,今天带来的是nullptr空指针常量.constexpr(常量表达式)C++的两个用法.Result result_fun = nullptr;constexpr stati ...

  8. c++11 常量表达式

    c++11 常量表达式 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inclu ...

  9. C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)

    #include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...

随机推荐

  1. Javascript 日历插件

    1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点击下载    查看示例 2. DatePicker 这款日期插件支持单选.多选和 ...

  2. 兼容 .NET Core3.0, Natasha 框架实现 隔离域与热编译操作

    关于 Natasha    动态构建已经成为了封装者们的家常便饭,从现有的开发趋势来看,普通反射性能之低,会迫使开发者转向EMIT/表达式树等构建方式,但是无论是EMIT还是表达式树,都会依赖于反射的 ...

  3. Layer.js弹层的一些简单的使用

    //-----------这里只是简单的做一下记录,没有封装,作为笔记防止忘记了 //----contentMsg 里面是可以传入 HTML代码的 top.layer.alert(contentMsg ...

  4. PIE SDK坐标转换算法

    1.算法功能简介 数据坐标转换是空间实体位置的位置描述,是从一种坐标系统变换到另一坐标系统的过程.通过建立两个坐标系之间一一对应关系来实现.是各种比例尺地图测量和编绘中建立地图数学基础必不可少的步骤. ...

  5. MySQL语言分类——DML

    DML DML的全称是Database management Language,数据库管理语言.主要包括以下操作: insert.delete.update.optimize. 本篇对其逐一介绍 IN ...

  6. i春秋——“百度杯”CTF比赛 九月场——123(Apache解析pht,phtml,php3,phps等 php别名)

    网页源码提示用户信息在user.php中,直接访问是不会有显示的,于是查找相应的备份文件,vim交换文件等,最后发现/user.php.bak 用burp采用如下配置开始爆破 最后爆破出两个账号 登录 ...

  7. 简单几招提速 Kotlin Kapt编译

    https://droidyue.com/blog/2019/08/18/faster-kapt/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_ ...

  8. 基于Spring Boot的注解驱动式公众号极速开发框架FastBootWeixin

    本框架基于Spring Boot实现,使用注解完成快速开发,可以快速的完成一个微信公众号,重新定义公众号开发. 在使用本框架前建议对微信公众号开发文档有所了解,不过在不了解公众号文档的情况下使用本框架 ...

  9. VMware vSphere API开发(一)---vSphere 体系核心概念

    1.VMware SDDC        VMware 软件定义数据中心(software defined dataCenter,SDDC),包括了从最底层的VMware vSphere.软件定义存储 ...

  10. css 固定图片