编译期计算

        关于编译期计算,直接能够想到的应用是决定是否启用某个模板,或者多个模板之间做选择。但如果有足够多的信息,编译器甚至可以计算控制流的结果。

模板元编程

        模板元编程的简单例子,如下:

#include <iostream>template <unsigned p, unsigned d>
struct DoIsPrimer {static constexpr bool value = (p % d != 0) && DoIsPrimer<p, d - 1>::value;
};template <unsigned p>
struct DoIsPrimer<p, 2> {static constexpr bool value = (p % 2 != 0);
};template <unsigned p>
struct IsPrimer {static constexpr bool value = DoIsPrimer<p, p / 2>::value;
};template <>
struct IsPrimer<0> { static constexpr bool value = false; };template <>
struct IsPrimer<1> { static constexpr bool value = false; };template <>
struct IsPrimer<2> { static constexpr bool value = true; };template <>
struct IsPrimer<3> { static constexpr bool value = true; };//仅为证明自己算发生在编译阶段
template <unsigned p, typename enable = typename std::enable_if<IsPrimer<p>::value>::type>
class Primer {
};int main(int argc, char **argv)
{Primer<3> primer3;Primer<9> primer9;return 0;
}

        上面的例子Primer<3> primer3;通过编译,Primer<9> primer9;编译报错,这足以证明IsPrimer在编译阶段完成了计算,其展开步骤如下:

    IsPrime<9>::value
=>  DoIsPrime<9,4>::value
=>  9%4!=0 && DoIsPrime<9,3>::value
=>  9%4!=0 && 9%3!=0 && DoIsPrime<9,2>::value
=>  9%4!=0 && 9%3!=0 && 9%2!=0
=>  false  

通过 constexpr 进行计算

        c++11开始引入了constexpr特性,大大简化了编译器运算。但对于constexpr使用,c++11拥有诸多限制,如constexpr的定义只能包含一个return语句。这些限制从c++14开始,大部分被移除。但为了所有计算步骤都能够在编译其进行,目前所有的c++版本constexpr函数都不支持异常抛出和内存分配。下面是constexpr版的IsPrimer:

#include <iostream>#if 1
//c++11实现
constexpr bool DoIsPrimer(unsigned p, unsigned d) { return d != 2 ? (p % d != 0) && DoIsPrimer(p, d - 1) : (p % 2 != 0); }constexpr bool IsPrimer(unsigned p) { return p < 4 ? !(p < 2) : DoIsPrimer(p, p / 2); }
#else
//c++14实现
constexpr bool IsPrimer(unsigned p) {for (unsigned d = 2; d < p / 2; ++d) {if (p % d == 0)return false;}return p > 1;
}
#endif//仅为证明自己算发生在编译阶段
template <unsigned p, typename enable = typename std::enable_if<IsPrimer(p)>::type>
class Primer {
};int main(int argc, char **argv)
{Primer<3> primer3;Primer<9> primer9;return 0;
}

         需要注意一点,“可以”在编译期计算,并非“一定”在编译期计算。计算发生在编译还是运行,先来做一个实验:

//...
bool is_p0 = IsPrimer(0); //==在编译期计算
const bool is_p1 = IsPrimer(1); //==在编译期计算
constexpr bool is_p2 = IsPrimer(2); //==在编译期计算
int p3 = 3;
bool is_p3 = IsPrimer(p3); //在运行期计算
int p4 = 4;
const bool is_p4 = IsPrimer(p4); //在运行期计算
const int p5 = 5;
constexpr bool is_p5 = IsPrimer(p5); //==在编译期计算int main(int argc, char **argv)
{Primer<3> primer3;bool is_p6 = IsPrimer(6); //在运行期计算int p7 = 7;bool is_primer = IsPrimer(p7); //在运行期计算const int p8 = 8;constexpr bool is_p8 = IsPrimer(p8); //==在编译期计算return 0;
}

        关于上面的实验, 通过在IsPrime设置断点即可判断计算发生在编译期还是运行期。通过实验,可以发现下面的规律:

  • 如果计算在全局,结果变量为constexpr或输入参数为常量,计算都会在编译期触发
  • 如果计算在局部,结果变量为constexpr并且输入参数为常量,计算才会在编译期触发,否则延迟到运行期

编译器if

        c++17引入了if constexpr(...)语法,编译器根据该语法在编译期决定使用if部分还是else部分的代码。该语法主要用于两个场景:

  • 变参模板

        使用编译器if判断参数数量,当参数为0时,不再执行任何语句,解除对void print(void)函数的依赖。此处编译期if不可被普通if取代,print函数族的迭代生成实在编译器完成的,如果使用普通if,编译时会报错“No matching function for call to 'print' ”,因为print模板不会生成参数为0的函数,当编译迭代到...args为0时,发现没有合适的函数调用报错。

#include <iostream>template <typename T, typename ...Ts>
void print(const T &arg, const Ts &...args)
{std::cout << arg << std::endl;if constexpr(sizeof...(args) > 0) {print(args...);}
}int main(int argc, char **argv)
{print("Jim", 'M', 30);return 0;
}
  • std::is_xxx函数族

        通过使用std::is_xxx函数族判断,不同的的情况使用不同的代码。下面是一个例子,如果T是整形族,则执行递归;否则直接返回原值。此处是否可用普通if语句代替编译器if语句?答案是否定的。如果使用普通if语句,编译时,整个函数的所有语句都会被编译,当类型为string时,由于不支持和整型的比较,及-和*操作符,arg == 1和arg * factorial(arg - 1)都会报错。

template <typename T>
constexpr T factorial(T arg)
{if constexpr(std::is_integral_v<T>) {if (arg == 1)return arg;return arg * factorial(arg - 1);}else {///static_assert(false, "argument is not integral!"); ///编译期总是会被触发,不管是否会用到该段代码static_assert(!std::is_integral_v<T>, "argument is not integral!"); //总是不会被触发,即便该段代码被用到std::cout << arg<< std::endl;return arg;}
}int main(int argc, char **argv)
{std::cout << factorial(5) << std::endl;std::cout << factorial("hello") << std::endl;return 0;
}

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

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

相关文章

vue練習--prop

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Prop練習</title> <!-- 开发环境版本…

mysql面试(二)

前言 这是mysql面试基础的第二节&#xff0c;主要是了解一下mysql数据更新的基本流程&#xff0c;还有三大日志的作用。但是具体的比如undolog是如何应用在mvcc机制中的&#xff0c;由于篇幅问题就放在下一在章节 数据更新流程 上面是说了更新真正数据之前的大致流程&#x…

requets库传data和传json的区别

传data和传json的qubie 被测对象&#xff0c;白月黑羽系统 系统下载地址&#xff1a; https://www.byhy.net/prac/pub/info/bysms/ 测试用例下载地址&#xff1a; https://cdn2.byhy.net/files/selenium/testcases.xlsx 一、传data import json import requests import pytes…

7、Qt5开发及实列(笔记3-系统操作)

说明&#xff1a;此示例包含了基本的常使用的系统操作 效果如下: mainwindos.cpp #pragma execution_character_set("utf-8") #include "mainwindow.h"#include <QDesktopWidget> #include <QApplication> #include <QHostInfo> #in…

uni-app renderjs 的使用

在 uni-app 中使用 renderjs 的一般步骤如下&#xff1a; 在原有 <script> 下方添加一个新的 <script> 标签&#xff0c;用于编写 renderjs 代码。其中&#xff0c; lang 属性需设置为 "renderjs" &#xff0c; module 属性的值可以任意指定&#xff…

迁移学习:跨领域知识迁移的艺术

迁移学习&#xff1a;跨领域知识迁移的艺术 在机器学习的世界里&#xff0c;迁移学习&#xff08;Transfer Learning&#xff09;是一种强大的技术&#xff0c;它允许模型将在一个任务上学到的知识迁移到另一个相关任务上。这种方法在数据稀缺或计算资源受限的情况下尤其有用。…

【JAVA】Hutool CollUtil.sort 方法:多场景下的排序解决方案

在 Java 开发中&#xff0c;集合的排序是常见需求。Hutool 库的 CollUtil.sort 方法提供了一系列用于排序的实用功能&#xff0c;适用于不同的场景。以下是对几种常见场景及其实现方式的总结&#xff1a; <dependency><groupId>org.dromara.hutool</groupId>…

Go基础编程 - 10- 接口(interface)

接口&#xff08;interface&#xff09; 1. 接口1.1. 接口的定义1.2. 接口的实现1.3. 接口的变量类型1.4. 值接收者和指针接收者实现接口的区别1.4.1. 值接收者实现接口1.4.2. 指针接收者实现接口 2. 类型与接口的关系3. 接口的嵌套4. 空接口4.1. 空接口的应用 上一篇&#xff…

docker基础镜像

一、配置 docker 本地源 [docker-ce-stable] nameDocker CE Stable baseurlhttp://10.35.186.181/docker-ce-stable/ enabled1 gpgcheck0 配置阿里云Docker Yum源 yum install -y yum-utils device-mapper-persistent-data lvm2 git yum-config-manager --add-repo http://mirr…

探索PyCharm的调试技巧:深入变量的神秘世界

探索PyCharm的调试技巧&#xff1a;深入变量的神秘世界 在软件开发的过程中&#xff0c;调试是不可或缺的一部分。它帮助开发者理解代码的执行流程&#xff0c;查找并修复错误。PyCharm&#xff0c;作为一款功能强大的IDE&#xff0c;提供了一整套调试工具&#xff0c;让我们能…

动态数据下的稳定器:在线学习在目标检测中的适应之道

动态数据下的稳定器&#xff1a;在线学习在目标检测中的适应之道 在线学习&#xff08;Online Learning&#xff09;是一种灵活的机器学习范式&#xff0c;它允许模型通过连续学习新样本来适应数据分布的变化。这对于目标检测系统来说至关重要&#xff0c;因为它们需要在不断变…

Windows安装Visual Studio2022 + QT5.15开发环境

最近&#xff0c;把系统换成了Windows11&#xff0c;想重新安装QT5.12&#xff0c;结果发现下载不了离线安装包。 最后索性安装QT5.15了&#xff0c;特此记录下。 预祝大家&#xff1a;不论是何时安装&#xff0c;都可以安装到指定版本的QT。 一、VS2022安装 VS2022官网下…

ubuntu 22.04 安装部署gitlab详细过程

目录 gitlab介绍 gitlab安装 步骤1&#xff1a;更新系统 步骤2&#xff1a;添加 GitLab 的 GPG 密钥 gitlab企业版 gitlab社区版 步骤3&#xff1a;安装 GitLab 社区版 社区版 步骤4&#xff1a;初始化 GitLab 步骤5&#xff1a;访问 GitLab 步骤6&#xff1a;查看r…

C++ - 基于多设计模式下的同步异步⽇志系统

1.项目介绍 项⽬介绍 本项⽬主要实现⼀个⽇志系统&#xff0c; 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.开发环境 • Cent…

【专家系统】系统地掌握专家系统的基本概念、技术原理、实现方法以及应用实践。

一、基础概念与理论 专家系统基础 定义&#xff1a;专家系统是一种人工智能程序&#xff0c;它利用专家知识和推理能力来解决特定领域中的复杂问题。特点&#xff1a;具有专业知识、可解释性和可靠性。组成部分&#xff1a; 知识库&#xff1a;存储特定领域的专家知识&#x…

AI学习记录 - 导数在神经网络训练中的作用(自己画的图,很丑不要介意!)

导数的作用 我们去调整神经网络的权重&#xff0c;一般不会手动去调整&#xff0c;如果只有很少的神经元&#xff0c;人工调整确实可以实现&#xff0c;当我们有几十层&#xff0c;一层几百上千个神经元的时候&#xff0c;人工调整就不可能了。 一个权重的调整涉及到两个问题&…

TCP的FIN报文可否携带数据

问题发现&#xff1a; 发现FTP-DATA数据传输完&#xff0c;TCP的挥手似乎只有两次 实际发现FTP-DATA报文中&#xff0c;TCP层flags中携带了FIN标志 piggyback FIN 问题转化为 TCP packet中如果有FIN flag&#xff0c;该报文还能携带data数据么&#xff1f; 答案是肯定的 RFC7…

Go协程的运行机制以及并发模型

进程与线程 进程与线程都是os用来运行程序的基本单元。其中进程是正在执行的程序的实例&#xff0c;它包含了程序代码、数据、文件和系统资源等。进程是os资源分配的基本单元&#xff0c;每个进程都有自己独立的地址空间、文件描述符、网络连接、进程ID等系统资源。进程与进程…

【LeetCode:3098. 求出所有子序列的能量和 + 记忆化缓存】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十七章 字符设备和杂项设备总结回顾

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…