C++11/C++14中constexpr的使用案例详解

 更新时间:2023年06月09日 16:24:33   作者:fengbingchun  
C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,这篇文章主要介绍了C++11/C++14中constexpr的使用,需要的朋友可以参考下

      常量表达式(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

到此这篇关于C++11/C++14中constexpr的使用的文章就介绍到这了,更多相关C++ constexpr使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C语言中strcpy()函数与strncpy()函数的使用

    详解C语言中strcpy()函数与strncpy()函数的使用

    这篇文章主要介绍了详解C语言中strcpy()函数与strncpy()函数的使用,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • Windows安装Qt6.4.2及简单验证

    Windows安装Qt6.4.2及简单验证

    本文主要介绍了Windows安装Qt6.4.2及简单验证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • 关于C++面向对象设计的访问性问题详解

    关于C++面向对象设计的访问性问题详解

    这篇文章主要给大家介绍了关于C++面向对象设计的访问性问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • C语言实现运筹学中的马氏决策算法实例

    C语言实现运筹学中的马氏决策算法实例

    这篇文章主要介绍了C语言实现运筹学中的马氏决策算法,简单介绍了马氏决策的概念,并结合实例形式分析了C语言实现马氏决策算法的具体实现技巧,需要的朋友可以参考下
    2017-09-09
  • 用C语言实现三子棋小游戏

    用C语言实现三子棋小游戏

    这篇文章主要为大家详细介绍了用C语言实现三子棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Matlab绘制酷炫坐标区域的方法详解

    Matlab绘制酷炫坐标区域的方法详解

    这篇文章主要为大家详细介绍了如何利用Matlab编写一个能让坐标区域变得很炫酷的修饰函数,文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-05-05
  • 利用C++模拟实现STL容器:list

    利用C++模拟实现STL容器:list

    列表是一种顺序容器,它允许在序列中的任何位置执行常量时间插入和删除操作,并允许在两个方向上进行迭代。本文将利用C++模拟实现list,希望对大家有所帮助
    2022-12-12
  • 在C++17中实现无锁数据结构的方法详解

    在C++17中实现无锁数据结构的方法详解

    在探索 C++17 中的无锁数据结构之前,我们首先需要理解无锁编程的基本概念及其在现代软件开发中的重要性,在这个章节中,我们将深入探讨无锁编程的概念,以及它如何满足人类对于更高效、更可靠软件的本能需求,文中通过代码示例介绍的非常详细,感兴趣的朋友可以参考下
    2023-12-12
  • VScode上配置 c语言环境的图文教程

    VScode上配置 c语言环境的图文教程

    这篇文章主要介绍了配置VScode c语言环境,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 利用C/C++实现较完整贪吃蛇游戏

    利用C/C++实现较完整贪吃蛇游戏

    这篇文章主要为大家详细介绍了利用C/C++实现较完整贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03

最新评论