C++学习笔记----8、掌握类与对象(三)---- CONSTEXPR与CONSTEVAL

ops/2024/10/11 7:33:38/

        在现代c++中,在编译时而不是运行时容易地执行计算是可能的。这提高了代码的运行时性能。有两个重要的关键字用于完成这个:constexpr与consteval。

1、constexpr关键字

        c++总是有常量表达式的符号,它们在编译时计算表达式。在有些情况下,要求是常量表达式。例如,当定义数组时,数组的大小需要是一个常量表达式。因为这种限制,下面的代码在c++中是无效的:

const int getArraySize()
{return 32;
}int main()
{int myArray[getArraySize()];	 //ERROR: Invalid in C++println("Size of array = {}", size(myArray));
}

        使用constexpr关键字,getArraySize()可以被重新定义允许从一个常量表达式内返回:

constexpr int getArraySize()
{return 32;
}
int main()
{int myArray[getArraySize()];	 // OKprintln("Size of array = {}", size(myArray));
}

        甚至可以像下面这样做:

int myArray[getArraySize() + 1];    // OK

        常量表达式只可用于constexpr实体与整数,布尔型,字符型,与枚举常量。

        声明一个constexpr函数就包含了函数能做什么的限制,因为编译器需要能够在编译时评测函数。例如,constexpr函数不允许有副作用,也不能让例外逃出函数。在函数中抛出例外与在try代码块中获取是允许的。constexpr函数允许无条件地调用其它的constexpr函数。也允许调用non-constexpr函数,但是只有在运行时评测过程中触发的调用才可以,而在常量评测过程中的不行。例如:

void log(string_view message)
{print("{}", message);
}constexpr int computeSomething(bool someFlag)
{if (someFlag){log("someFlag is true");return 42;}else{return 84;}
}

        computeSomething()函数是一个constexpr且包含一个对于log()的调用,它是一个non-constexpr,但是该调用只是在someFlag为true时才执行。只要computeSomething()使用被设置成false的someFlag进行调用,就可以在常量表达式内被调用,例如:

constexpr auto value1 { computeSomething(false) };

        将someFlag设置成true调用这个函数就不能在常量表达式中完成。下面的代码编译不成功:

constexpr auto value2 { computeSomething(true) };

        而下面的代码却可以,因为评测现在发生在运行时而不是编译时:

const auto value3 { computeSomething(true) };

        c++23对constexpr函数放松了一点点:goto语句,lables(除了case labels),static和static constexpr变量现在允许在constexpr函数中,以前是不可以的。

2、consteval关键字

        constexpr关键字指出一个函数可以在编译时执行,但是不保证编译时执行。看下面的constexpr函数:

constexpr double inchToMm(double inch) { return inch * 25.4; }

        如果像下面这样调用,函数在编译时评测是如你所愿的:

constexpr double const_inch { 6.0 };
constexpr double mm1 { inchToMm(const_inch) };    // at compile time

        然而,如果像下面这样调用,函数在编译时不评测,而是在运行时评测!

double dynamic_inch { 8.0 };
double mm2 { inchToMm(dynamic_inch) };    // at run time

        如果你确实想要一个函数总是在编译时评测,需要使用consteval关键字,让函数变成即刻函数。inchToMm()函数可以修改如下:

consteval double inchToMm(double inch) { return inch * 25.4; }

        现在,使用前面定义的mm1调用inchToMm()仍然编译良好,进行编译时评测。然而,用定义的mm2进行调用就会编译错误,因为它无法在编译时评测。

        即刻函数只可以在常量评测时调用。例如,假设你在如下的即刻函数:

consteval int f(int i) { return i; }

        该即刻函数可以从constexpr函数中调用,但是仅仅在constexpr函数在常量评测过程中被执行。例如,下面的函数使用了一个if consteval语句来检查常量评测是否发生,在这种情况下,它可以调用f()。而在else分支,f()不能被调用。

constexpr int g(int i)
{if consteval { return f(i); }else{ return 42; }}

3、constexpr与consteval类

        通过定义一个constexpr或者consteval构造函数,可以生成用户定义的类型的常量表达式变量。与constexpr函数一样,constexpr类在编译时可以被评测也可以不被评测,而consteval类保证在编译时被评测。

        下面的Matrix类定义了一个constexpr构造函数。它也定义了一个constexpr getSize()成员函数来执行一些计算。

class Matrix
{
public:Matrix() = default; // Implicitly constexprconstexpr explicit Matrix(unsigned rows, unsigned columns): m_rows{ rows }, m_columns{ columns }{}constexpr unsigned getSize() const{return m_rows * m_columns;}private:unsigned m_rows{ 0 }, m_columns{ 0 };
};

        使用这个类来声明constexpr对象是直接的:

constexpr Matrix matrix { 8, 2 };
constexpr Matrix matrixDefault;

        这样的一个constexpr对象现在可以使用,例如,生成一个足够大的数组来保存线性形式的矩阵:

int linearizedMatrix[matrix.getSize()];    // OK

        编译器生成的(隐式或显式使用=default)成员函数,比如缺省构造函数,析构函数,赋值操作符,等等,会自动成为constexpr,除非类包含数据成员,而这些成员函数不是constexpr。

        constexpr与consteval成员函数的定义要对编译器可见,以便它们可以在编译时评测。这意味着如果类在模块中定义,这样的成员函数也必须定义在模块接口文件中,而不是在模块实现文件中。

        注意:有些标准库中的类是constexpr,所以可以在其他constexpr函数与类中使用。例如std::vector,optional,string,unique_ptr,bitset,与variant。


http://www.ppmy.cn/ops/123863.html

相关文章

element ui input textarea控制显示高度

样式代码 .testPage { position: absolute; left: 0; top: 0; right: 0; bottom: 0; display: flex; height: 100%; /* 控制输入框高度 */ .el-textarea { height: 90%; ::v-deep .el-textarea__inner { height: 90%; } } }

ES6 WeakMap 详解

引言 JavaScript 作为一门强大的编程语言,在开发中充满了各种各样的数据结构和功能。而 WeakMap 正是其中一个神秘而强大的存在。WeakMap 是在 ES6 标准中引入的数据结构,它不同于普通的 Map,具有独特的特点和用途。 WeakMap 与其他数据结构…

代码随想录算法训练营总结

这几天一直有事情需要忙,所以现在来准备总结以下训练营的成果。 先说以下总体感受,非常值得!!! 从两个月前开始跟着每天看发布的任务,然后每天坚持打卡,收获还是很大的,从数组开始…

UART在Linux内核启动时突然不打印的问题

国庆前一天收到的任务,在一颗比较成熟的芯片的SDK基础上,移植一个新内核,让它能够在bitfile下跑在FPGA上。 看了芯片设计那边给的文档,对比过去的那颗,感觉也就改改寄存器,中断号,时钟&#xff…

网站开发基础:HTML、CSS

前端开发主要使用的技术如 HTML、CSS 和 JavaScript 等。 简单制作一个网页 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>柒毓同学网站的首页</title><style>.c1{border: solid 1px g…

【uniapp小程序】使用cheerio去除字符串中的HTML标签并获取纯文本内容

【uniapp小程序】使用cheerio去除字符串中的HTML标签并获取纯文本内容 参考资料安装引入使用 参考资料 【博主&#xff1a;AIpoem】uniapp小程序 使用cheerio处理网络请求拿到的dom数据 cheerio文档&#xff1a;https://github.com/cheeriojs/cheerio/wiki/Chinese-README 安…

JAVA开发中的常用通讯协议

在JAVA开发中&#xff0c;通讯协议是实现不同系统或组件之间数据交换的基础。随着分布式系统和微服务架构的流行&#xff0c;掌握常用的通讯协议对于JAVA开发者来说至关重要。本文将介绍在JAVA开发中常用的几种通讯协议&#xff0c;以及它们的特点和应用场景。 1. HTTP/HTTPS …

kotlin 委托

一、类委托 interface DB{fun insert() } class SqliteDB : DB {override fun insert() {println(" SqliteDB insert")} }class MySql : DB{override fun insert() {println(" MySql insert")} }class OracleDB : DB{override fun insert() {println(&quo…