C++11/C++14中constexpr的使用

news2024/11/15 13:55:33

      常量表达式(const expression)是指值不会改变并且在编译过程中就能得到计算结果的表达式。字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

      只要有可能使用constexpr,就使用它

      C++11中constexpr的使用

      constexpr是C++11中添加的一个特性,其主要思想是通过在编译时而不是运行时进行计算来提高程序的性能,将时间花在编译上,而在运行时节省时间(类似于模版元编程)。

      C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。

      尽管不能使用普通函数作为constexpr变量的初始值,但C++11标准允许定义一种特殊的constexpr函数。这种函数应该足够简单以使得编译时就可以计算其结果,这样就能用constexpr函数去初始化constexpr变量了。

      一般来说,如果你认定变量是一个常量表达式,那就把它声明成constexpr类型

      所有constexpr对象都是const对象,而并非所有的const对象都是constexpr对象。如果你想让编译器提供保证,让变量拥有一个值,用于要求编译期常量的语境,那么能达到这个目的的工具是constexpr,而非const。

      constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法与其它函数类似,不过要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。constexpr函数或构造函数被隐式地指定为内联函数。

      constexpr函数体内也可以包含其它语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明

      允许constexpr函数的返回值并非一个常量。constexpr函数不一定返回常量表达式。

      和其它函数不一样,内联函数和constexpr函数可以在程序中多次定义。不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中

      constexpr函数只能调用其它constexpr函数,不能调用简单函数(simple function)。constexpr函数不应该是void类型。constexpr函数中不允许有前缀增量(++i),在C++14中已删除此限制。

      constexpr函数的理解

      (1).constexpr函数可以用在要求编译期常量的语境中。在这样的语境中,若你传给一个constexpr函数的实参值是在编译期已知的,则结果也会在编译期间计算出来。如果任何一个实参值在编译期未知,则你的代码将无法通过编译。

      (2).在调用constexpr函数时,若传入的值有一个或多个在编译期未知,则它的运作方式和普通函数无异,亦即它也是在运行期执行结果的计算。这意味着,如果函数执行的是同样的操作,仅仅应用的语境一个是要求编译期常量的,一个是用于所有其它值的话,那就不必写两个函数。constexpr函数就可以同时满足所有需求。

      constexpr函数仅限于传入和返回字面类型(literal type),意思就是这样的类型能够持有编译期可以决议的值。在C++11中,所有的内建类型,除了void,都符合这个条件。但是用户自定义类型同样可能也是字面类型,因为它的构造函数和其它成员函数可能也是constexpr函数。

      在C++11中,constexpr函数都隐式地被声明为const。

      以下为测试代码:

namespace {

// constexpr function: constexpr函数被隐式地指定为内联函数
constexpr int new_sz() { return 42; }
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
constexpr int product(int x, int y) { return (x * y); }

// pow前面写的那个constexpr并不表明pow要返回一个const值,它表明的是如果base和exp是编译期常量,pow的返回结果
// 就可以当一个编译期常量使用;如果base和exp中有一个不是编译期常量,则pow的返回结果就将在执行期计算
constexpr int pow(int base, int exp) noexcept
{
	return (exp == 0 ? 1 : base * pow(base, exp - 1));
}

} // namespace

int test_constexpr_1()
{
	// constexpr variables
	constexpr int mf = 20; // 20 is a constant expression
	constexpr int limit = mf + 1; // mf + 1 is a constant expression
	
	constexpr int foo = new_sz(); // foo is a constant expression
	std::cout << "foo:" << foo << "\n"; // foo:42

	// 当scale的实参是常量表达式时,它的返回值也是常量表达式;反之则不然
	int arr[scale(2)]; // ok
	int i = 2;
	//int a2[scale(i)]; // error: scale(i)不是常量表达式
	size_t value = scale(i); // ok,constexpr函数不一定返回常量表达式
	std::cout << "value:" << value << "\n"; // value:84

	int sz = 1;
	//constexpr auto array_size = sz; // error, sz的值在编译期未知
	const auto array_size1 = sz; // ok, array_size1是sz的一个const副本

	int arr2[product(2, 3)] = { 1, 2, 3, 4, 5, 6 };
	std::cout << "arr2[5]:" << arr2[5] << "\n"; // arr2[5]:6

	return 0;
}

      constexpr构造函数:尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数。事实上,一个字面值常量类必须至少提供一个constexpr构造函数。

      constexpr构造函数可以声明成=default的形式(或者是删除函数的形式=delete)。否则,constexpr构造函数就必须既符合构造函数的要求(意味着不能包含返回语句),又符合constexpr函数的要求(意味着它能拥有的唯一可执行语句就是返回语句)。综合这两点可知,constexpr构造函数体一般来说应该是空的。我们通过前置关键字constexpr就可以声明一个constexpr构造函数了。

      constexpr构造函数必须初始化所有数据成员,初始值或者使用constexpr构造函数,或者是一条常量表达式。

      constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型。

      以下为测试代码:

namespace {

class Debug {
public:
	// constexpr构造函数必须初始化所有数据成员
	constexpr Debug(bool b = true) noexcept : hw_(b), io_(b), other_(b) {}
	constexpr Debug(bool h, bool i, bool o) noexcept : hw_(h), io_(i), other_(o) {}

	constexpr bool any() const noexcept { return hw_ || io_ || other_; }

	constexpr bool get_hw() const noexcept { return hw_; }
	constexpr bool get_io() const noexcept { return io_; }
	constexpr bool get_other() const noexcept { return other_; }

	void set_hw(bool b) noexcept { hw_ = b; }
	void set_io(bool b) noexcept { io_ = b; }
	void set_other(bool b) noexcept { other_ = b; }
	//constexpr void set_hw(bool b) noexcept { hw_ = b; } // C++14
	//constexpr void set_io(bool b) noexcept { io_ = b; }
	//constexpr void set_other(bool b) noexcept { other_ = b; }

private:
	bool hw_, io_, other_;
};

constexpr Debug hw_debug(const Debug& d1, const Debug& d2) noexcept
{
	return d1.get_hw() && d2.get_hw(); // 调用constexpr成员函数
}

} // namespace

int test_constexpr_2()
{
	constexpr Debug debug(false, true, false);
	if (debug.any())
		std::cout << "any true" << std::endl; // will output

	if (debug.get_io())
		std::cout << "get_io true" << "\n"; // will output

	constexpr Debug prod(false);
	if (prod.any())
		std::cout << "any true" << std::endl; // will not output

	constexpr auto hw = hw_debug(debug, prod); // 使用constexpr函数的结果来初始化constexpr对象
	std::cout << "hw:" << hw.get_hw() << "\n"; // hw:0

	return 0;
}

      注:以上内容主要整理自:《C++ Primer Fifth Edition》、《Effective Modern C++》

      C++14中constexpr的使用

      在C++11中,constexpr函数只能包含一组非常有限的语法,包括但不限于:typedefs、using和一条返回语句。在C++14中,允许的语法集大大扩展,包括最常见的语法,如if语句、多次返回、while或for循环等

      以下为测试代码:

namespace {

// C++14 constexpr functions may use local variables and loops
constexpr int pow2(int base, int exp) noexcept
{
	auto result = 1;
	for (int i = 0; i < exp; ++i) result *= base;
	return result;
}

constexpr unsigned int factorial(unsigned int n) {
	if (n <= 1)
		return 1;
	else
		return n * factorial(n - 1);
}

} // namespace

int test_constexpr_14_1()
{
	constexpr auto value = pow2(2, 4);
	std::cout << "pow2 value:" << value << "\n"; // pow2 value:16

	constexpr auto value2 = factorial(5);
	std::cout << "factorial value:" << value2 << "\n"; // factorial value:120

	return 0;
}

      执行结果如下:

      GitHub:https://github.com/fengbingchun/Messy_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/605684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MKS SERVO4257D 闭环步进电机_系列7 MODBUS-RTU通讯示例

第1部分 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口和RS485/CAN串行接口&#xff0c;支持MODBUS-RTU通讯协议&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&a…

论坛项目学习记录【预备篇2】

论坛项目学习记录【预备篇2】 1. 什么是依赖注入2. 怎么使用依赖注入依赖注入注意事项 3.组件扫描情况下依赖注入的实现4.Resource注解的使用与Autowired的区别5.SpringMvcSpringMvc执行流程 1. 什么是依赖注入 就是在Spring容器内容将各个对象的依赖关系建立好的操作&#xf…

持续集成和持续交付:构建高效的软件交付流水线

在现代软件开发中&#xff0c;持续集成&#xff08;Continuous Integration&#xff09;和持续交付&#xff08;Continuous Delivery&#xff09;已成为构建高效、可靠软件交付流水线的关键实践。通过自动化和频繁地集成代码、构建、测试和部署&#xff0c;团队能够更快地交付高…

Unity--使用Cinemachine Confiner设置摄像机边界

使用Cinemachine Confiner设置摄像机边界 前提提要&#xff1a;在做这个功能前需要&#xff1a; ​ main camera ​ 另外一个相机 思路&#xff1a;创建一个对象绑定Polygon Collider2D 边界。然后在另外一个相机Cinemachine Confiner上绑定他 ​ 绑定边界 记得点这个&#…

2023年京东618全品类预售数据查询

这一期主要分享今年618京东数码产品的预售数据&#xff0c;包括笔记本电脑、投影机、微单相机三大品类。 -笔记本电脑- 今年618&#xff0c;笔记本电脑在京东累计预售量达到73万件&#xff0c;预售额达到41亿元。预售期间&#xff0c;微单相机品类均价在5661元左右。期间&#…

软件测试实验:链接测试

目录 前言实验目的实验环境实验内容实验步骤实验过程总结 前言 本实验的目的是学习和掌握软件测试中的链接测试方法和技巧。链接测试是指对Web应用系统中的各种链接进行检查和验证&#xff0c;以确保它们能正确地指向预期的目标&#xff0c;不出现错误链接、空链接、死链接或孤…

简单尝试:ChatGLM-6B + Stable diffusion管道连接

核心思想是&#xff1a; 1. 抛去算法设计方面&#xff0c;仅从工程角度考虑的话&#xff0c;Stable diffusion的潜力挖掘几乎完全受输入文字影响。 2. BLIP2所代表的一类多模态模型走的路线是"扩展赋能LLM模型"&#xff0c;思路简单清晰&#xff0c;收益明显。LLM …

电池状态估计 | Matlab实现利用卡尔曼滤波器估计电池充电状态

文章目录 效果一览文章概述研究内容程序设计参考资料效果一览 文章概述 电池状态估计 | Matlab实现利用卡尔曼滤波器估计电池充电状态 研究内容 目前,常用的电池模型有:数

外包干了5年,女朋友嫌弃我,跑了。。。

先说一下自己的情况。大专生&#xff0c;17年通过校招进入湖南某软件公司&#xff0c;干了接近5年的测试&#xff0c;今年年上旬&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01;而我已经在一个企业干了5年&#xff0c;…

一文带你了解MySQL之约束

在SQL标准中&#xff0c;一共规定了6种不同的约束&#xff0c;包括非空约束&#xff0c;唯一约束和检查约束等&#xff0c;而在MySQL中是不支持检查约束的&#xff0c;所以这篇文章先对其余5种约束做一个详解和练习。 文章目录 1. 约束的概念2. 约束的分类3. 非空约束4. 唯一约…

大数据:pyspark模块,spark core的RDD,RDD是弹性分布式数据抽象对象,RDD五大特性,wordcount案例展示RDD

大数据&#xff1a;pyspark模块 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql…

English Learning - L3 作业打卡 Lesson4 Day29 2023.6.2 周五

English Learning - L3 作业打卡 Lesson4 Day29 2023.6.2 周五 引言&#x1f349;句1: I once made a big mistake at the office and felt foolish.成分划分弱读连读爆破语调 &#x1f349;句2: I had egg on my face.成分划分弱读语调 &#x1f349;句3: Over the weekend, m…

Java企业级信息系统开发学习笔记13 Spring Boot(使用maven方式构建Spring Boot项目)

文章目录 一、学习目标二、Spring Boot框架概述三、使用maven方式构建Spring Boot项目&#xff08;一&#xff09;创建Maven项目&#xff08;二&#xff09;添加依赖&#xff08;三&#xff09;创建入口类&#xff08;四&#xff09;创建控制器&#xff08;五&#xff09;运行入…

防火墙之服务器负载均衡

防火墙之服务器负载均衡 原理概述&#xff1a; 防火墙&#xff08;英语&#xff1a;Firewall&#xff09;技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资料与信息安…

C语言---操作符详解

1、操作符分类 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号操作符 下标引用、函数调用和结构成员。 2、算数操作符 - * / %// 7 / 2 ----->3 // 7 % 2 ----->1//那如果想要打印…

Java程序设计入门教程--类的创建过程

构造方法 在Java的每个类中都有一种特殊的成员方法&#xff0c;它的方法名和类名是一致的。在创建对象时&#xff0c;调用这种特殊方法对成员变量进行初始化&#xff0c;这种方法称为构造方法。 注意 创建构造方法与创建成员方法的格式相同&#xff0c;但要注意以下几点&#…

3GPP R16 HST

3GPP R16高铁场景为了能够支持最高3.6GHz载波频率以及最高500km/h的速度&#xff0c;提出了增强RRM要求和终端解调能力要求。 在增强RRM要求的维度&#xff0c;对intra-RAT和inter-RAT测量提出了新的要求。其中&#xff0c;intra-RAT测量包括NR小区重选要求、NR小区识别要求、波…

大数据:hadoop spark,spark特点,功能,架构,模块,角色

大数据&#xff1a;hadoop spark 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sq…

把玩数据在内存中的存储

前言&#xff1a;时光如梭&#x1f4a6;&#xff0c;今天到了C语言进阶啦&#x1f60e;&#xff0c;基础知识我们已经有了初步认识&#xff0c; 是时候该拔高拔高自己了&#x1f63c;。 目标&#xff1a;掌握浮点数在内存的存储&#xff0c;整形在内存的存储。 鸡汤&#xff1a…

arcgis for javascript api4.26 本地tomcat部署,以及解决跨域访问问题

一、配置java_jdk以及tomcat arcgis for javascript api 部署到本地服务器&#xff0c;可以是iis&#xff0c;也可以是tomcat,我这里是部署到tomcat,所以就介绍一下tomcat上部署的步骤。 如果电脑上有本地服务器的&#xff0c;可以跳过这一章&#xff0c;直接从第二章开始看 下…