C++白皮书学习

decltype

C++ decltype用法详解-CSDN博客     <-参考文章

 用来在编译时期进行自动类型推导。引入decltype是因为auto并不适用于所有的自动类型推导场景,在某些特殊情况下auto用起来很不方便,甚至压根无法使用。

auto varName=value;
decltype(exp) varName=value;

  •  auto根据=右边的初始值推导出变量的类型,decltype根据exp表达式推导出变量的类型,跟=右边的value没有关系
  • auto要求变量必须初始化,这是因为auto根据变量的初始值来推导变量类型的,如果不初始化,变量的类型也就无法推导
  • 而decltype不要求,因此可以写成如下形式

decltype(exp) varName;

原则上将,exp只是一个普通的表达式,它可以是任意复杂的形式,但必须保证exp的结果是有类型的,不能是void;如exp为一个返回值为void的函数时,exp的结果也是void类型,此时会导致编译错误 

decltype 的几种形式

int x = 0;
decltype(x) y = 1;           // y -> int
decltype(x + y) z = 0;       // z -> int
const int& i = x;
decltype(i) j = y;           // j -> const int &
const decltype(z) * p = &z;  // *p  -> const int, p  -> const int *
decltype(z) * pi = &z;       // *pi -> int      , pi -> int *
decltype(pi)* pp = &pi;      // *pp -> int *    , pp -> int * *

推到规则

  • 如果exp是一个不被括号()包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,decltype(exp)的类型和exp一致
  • 如果exp是函数调用,则decltype(exp)的类型就和函数返回值的类型一致
  • 如果exp是一个左值,或被括号()包围,decltype(exp)的类型就是exp的引用,假设exp的类型为T,则decltype(exp)的类型为T&
     

exp中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码

class A{
public:int x;
}int main()
{
const A obj;
decltype(obj.x) a=0;//a的类型为int
decltype((obj.x)) b=a;//b的类型为int&int n=0,m=0;
decltype(m+n) c=0;//n+m得到一个右值,c的类型为int
decltype(n=n+m) d=c;//n=n+m得到一个左值,d的类型为int &
return 0;
}

左值:表达式执行结束后依然存在的数据,即持久性数据;右值:是指那些在表达式执行结束不再存在的数据,即临时性数据。一个区分的简单方法是:对表达式取地址,如果编译器不报错就是左值,否则为右值

移动语义

RVO(Return  Value Optimization)是一种编译器优化机制:当函数需要返回一个对象的时候,如果自己创建一个临时对象返回,那么这个临时对象会消耗一个构造函数(Constructor)、一个拷贝构造函数(Copy Constructor)以及一个析构函数(Destructor)的调用的代价,RVO的目的就是消除为保存返回值而创建的临时对象,这样就可以将成本降低到一个构造函数的代价

class Matrix {double* elements;    // 指向所有元素的指针// ...
public:Matrix (Matrix&& a)  // 移动构造{elements = a.elements;  // 复制句柄a.elements = nullptr;   // 现在 a 的析构函数不用做任何事情了}// ...
};

当用于初始化或赋值的源对象马上就会被销毁时,移动就比拷贝要更好:移动操作只是简单地把对象的内部表示“窃取”过来。&& 表示构造函数是一个移动构造函数Matrix&& 被称为右值引用。当用于模板参数时,右值引用的写法 && 被叫做转发引用

Matrix mx = m1+m2+m3;  // 不需要临时变量
string sx = s1+s2+s3;  // 不需要临时变量

右值引用可以用于给现有类方便地添加移动语义。意思是说,拷贝构造函数和赋值运算符可以根据实参是左值还是右值来进行重载。当实参是右值时,类的作者就知道他拥有对该实参的唯一引用。

一个突出的例子是生成“智能指针”的工厂函数:

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1)
{return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
}
template <class T>
class clone_ptr
{
private:T* ptr;
public:// ...clone_ptr(clone_ptr&& p)            // 移动构造函数: ptr(p.ptr)    // 拷贝数据的表示{p.ptr = 0;      // 把源数据的表示置空}clone_ptr& operator=(clone_ptr&& p) // 移动赋值{std::swap(ptr, p.ptr);return *this;   // 销毁目标的旧值}
};

资源管理指针

void newer_use(Args a)
{auto p = unique_ptr<Blob>(new Blob(a));// ...if (foo) throw Bad();  // 不会泄漏if (bar) return;       // 不会泄漏// ...
}

这种写法更简短、更安全,迅速就流行开去。不过,“智能指针”仍然被过度使用:“它们的确智能,但它们仍然是指针。”除非我们确实需要指针,否则,简单地使用局部变量会更好:

void simplest_use(Args a)
{Blob b(a);// ...if (foo) throw Bad(); // 不会泄漏if (bar) return;      // 不会泄漏// ...
}

nullptr

在 C 和 C++ 中,如果将字面量 0 赋值给指针或与指针比较时它表示空指针。更令人困惑的是,如果将任何求值为零的整数常量表达式赋值给指针或与指针比较时它也表示空指针。例如:

int* p = 99-55-44; // 空指针
int* q = 2;        // 错误:2 是一个 int,而不是一个指针

标准库宏 NULL(从 C 中采用),它在标准 C++ 中定义为 0。某些编译器会对 int* p = 0 提出警告

int* p0 = nullptr;
int* p1 = 99-55-44;  // 可以,为了兼容性
int* p2 = NULL;      // 可以,为了兼容性int f(char*);
int f(int);int x1 = f(nullptr); // f(char*)
int x2 = f(0);       // f(int)

constexpr 函数

constexpr 函数可以在编译期进行求值,因此它无法访问非本地对象(它们在编译时还不存在),因此 C++ 获得了一种纯函数。

为什么我们要求程序员应该使用 constexpr 来标记可以在编译期执行的函数?原则上,编译器可以弄清楚在编译期可以计算出什么,但是如果没有标注,用户将受制于各种编译器的聪明程度,并且编译器需要将所有函数体“永远”保留下来,以备常量表达式在求值时要用到它们。我们选择 constexpr 一词是因为它足够好记,但又“足够奇怪”而不会破坏现有代码。

在某些地方,C++ 需要常量表达式(例如,数组边界和 case 标签)。另外,我们可以通过将变量声明为 constexpr 来要求它在编译期被初始化:

constexpr LengthInKM marks[] = { LengthInMile(2.3), LengthInMile(0.76) };void f(int x)
{int y1 = x;constexpr int y2 = x;   // 错误:x 不是一个常量constexpr int y3 = 77;  // 正确
}

 C++之constexpr详解-CSDN博客

必须明确一点,在constexpr声明中如果定义了一个指针,限定符conxtexpr仅对指针有效,与指针所指的对象无关。

const int*p = nullptr;        //p是一个指向整形常量的指针
constexpr int* q = nullptr;   //q是一个指向整数的常量指针

属性

在程序中,属性提供了一种将本质上任意的信息与程序中的实体相关联的方法。例如:

[[noreturn]] void forever()
{for (;;) {do_work();wait(10s);}
}

属性 [[noreturn]] 通知编译器或其他工具 forever() 永远不会返回,这样它就可以抑制关于缺少返回的警告。属性用 [[…]] 括起来。

lambda

然而,我们可以指定 lambda 表达式应该从它的环境中“捕获”一些或所有的变量。回调是 lambda 表达式的一个常见用例,因为操作通常只需要写一次,并且操作会需要安装该回调的代码上下文中的一些信息。

void test()
{string s;// ... 为 s 计算一个合适的值 ...w.foo_callback([&s](int i){ do_foo(i,s); });w.bar_callback([=s](double d){ return do_bar(d,s); });
}

[&s] 表示 do_foo(i,s) 可以使用 ss 通过引用来传递(“捕获”)。[=s] 表示 do_bar(d,s) 可以使用 ss 是通过值传递的。如果回调函数在与 test 相同的线程上被调用,[&s] 捕获可能效率更高,因为 s 没有被复制。如果回调函数在不同的线程上被调用,[&s] 捕获可能是一个灾难,因为 s 在被使用之前可能会超出作用域;这种情况下,我们想要一份副本。一个 [=] 捕获列表意味着“将所有局部变量复制到 lambda 表达式中”。而一个 [&] 捕获列表意味着“lambda 表达式可以通过引用指代所有局部变量”,并意味着 lambda 表达式可以简单地实现为一个局部函数。

别名

template<typename A, typename B> class X { /* ... */ };
template<typename T> typedef X<T,int> Xi;  // 定义别名
Xi<double> Ddi;                            // 相当于 X<double, int>

typedef double (*analysis_fp)(const vector<Student_info>&);

using analysis_fp = double (*)(const vector<Student_info>&);

tuple

它主要用来返回成对的值,比如两个迭代器或者一个指针加上一个成功标志。 

元组是大小固定而成员类型可以不同的容器。作为一种通用的辅助工具,它们增加了语言的表现力。举几个元组类型一般用法的例子:

  • 作为返回类型,用于需要超过一个返回类型的函数
  • 编组相关的类型或对象(如参数列表中的各条目)成为单个条目
  • 同时赋多个值
auto SVD(const Matrix& A) -> tuple<Matrix, Vector, Matrix>
{Matrix U, V;Vector S;// ...return make_tuple(U,S,V);
};void use()
{Matrix A, U, V;Vector S;// ...tie(U,S,V) = SVD(A); // 使用元组形式
}

在这里,make_tuple() 是标准库函数,可以从参数中推导元素类型来构造 tupletie() 是标准库函数,可以把 tuple 的成员赋给有名字的变量。

静态类型

依赖静态类型安全有两大好处:

  • 明确意图
    • 帮助程序员直接表达想法
    • 帮助编译器捕获更多错误
  • 帮助编译器生成更好的代码。

noexcept规约

void do_something(int n) noexcept
{
    vector<int> v(n);
    // ...
}

如果 do_something() 抛异常,程序会被终止。这样操作恰好非常接近零开销,因为它简单地短路了通常的异常传播机制

还有一个条件版本的 noexcept,用它可以写出这样的模板,其实现依赖于某参数是否会抛异常。这是最初促成 noexcept 的用例。例如,下面代码中,当且仅当 pair 的两个元素都有不抛异常的移动构造函数时,pair 的移动构造函数才会声明不抛异常:

template<typename First, typename Second>
class pair {// ...template <typename First2, typename Second2>pair(pair<First2, Second2>&& rhs)noexcept(is_nothrow_constructible<First, First2&&>::value&& is_nothrow_constructible<Second, Second2&&>::value): first(move(rhs.first)),second(move(rhs.second)){}// ...
};

数字分隔符

auto a = 1'234'567;    // 1234567(整数)
auto b = 1'234'567s;   // 1234567 秒

概念

对 C++ 来说,泛型编程和使用模板的元编程已经取得了巨大的成功。但是,对泛型组件的接口却迟迟未能以一种令人满意的方式进行合适的规范。例如,在 C++98 中,标准库算法大致是如下规定的:

template<typename Forward_iterator, typename Value>
ForwardIterator find(Forward_iterator first, Forward_iterator last,const Value & val)
{while (first != last && *first != val)++first;return first;
}

C++ 标准规定:

  • 第一个模板参数必须是前向迭代器。
  • 第二个模板参数类型必须能够使用 == 与该迭代器的值类型进行比较。
  • 前两个函数参数必须标示出一个序列。

这些要求是隐含在代码中的:编译器所要做的就是在函数体中使用模板参数。结果是:极大的灵活性,对正确调用生成出色的代码,以及对不正确的调用有糟糕得一塌糊涂的错误信息。解决方案显而易见,将前两项条件作为模板接口的一部分来指定:

template<forward_iterator Iter, typename Value>requires equality_comparable<Value, Iter::value_type>
forward_iterator find(Iter first, Iter last, const Value& val);

未完待续......

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

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

相关文章

万字长文 详细讲述 计算机网络层

文章目录 网络层网络层的几个重要概念网络层的两个层面 网际协议 IP虚拟互连网络IP 地址IP 地址及其表示方法IP 地址与 MAC 地址地址解析协议 ARPIP 数据报的格式 IP层转发分组过程基于终点的转发最长前缀匹配 网际控制报文协议 ICMPICMP 报文的种类ICMP 的应用举例IPv6 的基本…

Discourse 未活动的用户是怎么处理的

Discourse 目前有一个参数为 clean up inactive users after days 来控制不活跃或者未激活的用户。 如果你的用户满足下面的条件的话&#xff0c;系统将会在到期后对用户进行清理和删除 从未在 Discourse 站点上发布任何内容 如果你在 Discourse 站点上发布了内容&#xff0c…

MYSQL导出数据

导出数据 备份数据   [rootsf105113 bin]# mysqldump -h127.0.0.1 -P3306 -uroot -p --add-locks -q dbname > dbname.sql //参数依次为:-h 主机 -p 端口 -u 用户名 -p 密码 --add-locks:导出过程中锁定表&#xff0c;完成后回解锁。 -q&#xff1a;不缓冲查询&#xf…

2024美赛数学建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&…

【PaperReading】5. Open-Vocabulary SAM

Category Content 论文题目 Open-Vocabulary SAM: Segment and Recognize Twenty-thousand Classes Interactively 作者 Haobo Yuan1 Xiangtai Li1 Chong Zhou1 Yining Li2 Kai Chen2 Chen Change Loy1 1S-Lab, Nanyang Technological University 2Shanghai Artificial In…

排序之希尔排序

希尔排序&#xff0c;也被称为缩小增量排序&#xff0c;是一种基于插入排序的算法。它通过比较相距一定间隔的元素&#xff0c;来工作&#xff0c;然后再逐渐减小间隔&#xff0c;直到整个数组排序完成。这种算法的主要优点是对于部分有序的数组&#xff0c;其效率非常高&#…

HBase实际应用中常见的问题 解决方案

HBase 是一个分布式的、面向列的开源数据库&#xff0c;通常用于处理大规模数据。在实际应用中&#xff0c;可能会遇到一些常见问题&#xff0c;以下是一些常见问题及其解决方案&#xff1a; 性能问题&#xff1a; 问题&#xff1a;HBase 性能下降&#xff0c;读写延迟增加。…

NLP论文阅读记录 - 05 | 2023 抽象总结与提取总结:实验回顾

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作2.1 提取方法2.2 抽象方法2.3 数据集 三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 Abstractive vs. Extractiv…

【OSG案例详细分析与讲解】之五:【3D交互式动画】

文章目录 一、【3D交互式动画】前言 二、【3D交互式动画】实现效果

ARP协议详解

1、ARP协议的定义 地址解析协议(Address Resolution Protocol&#xff0c;ARP)&#xff1a;ARP协议可以将IPv4地址(一种逻辑地址)转换为各种网络所需的硬件地址(一种物理地址)。换句话说&#xff0c;所谓的地址解析的目标就是发现逻辑地址与物理地址的映射关系。 ARP仅用于IPv…

c++ define 用法

PS&#xff1a;凡是以 # 开头的均为预处理指令&#xff0c;预处理又叫预编译。预编译不是编译&#xff0c;而是编译前的处理。这个操作是在正式编译之前由系统自动完成的。 宏定义 宏定义分为有参和午餐&#xff0c;一般而言无参更多用constexpr代替 无参 #define 标识符 …

代币中的decimal精度代表了什么

精度的意义在于允许发送小数的代币。举例&#xff0c;一个CAT代币合约的精度为6。那么 你拥有1个CAT就意味着合约中的balance 1 * 10^6 , 转账 0.1CAT出去的话&#xff0c;就需要输入 0.1*10^6 10^5。 也就时在涉及代币时&#xff0c;查询到的余额、转账的代币数量 都和 代币…

Sqoop入门指南:安装和配置

Sqoop是一个强大的工具&#xff0c;用于在Hadoop和关系型数据库之间高效传输数据。在本篇文章中&#xff0c;将深入探讨如何安装和配置Sqoop&#xff0c;以及提供详细的示例代码。 安装Java和Hadoop 在开始安装Sqoop之前&#xff0c;首先确保已经成功安装了Java和Hadoop。Sqo…

视频转音频小小代码

前言 特定的场景下&#xff0c;人的需求往往容易变得奇特&#xff0c;比方说&#xff0c;mp4的文件&#xff0c;有时候我只想听声音&#xff0c;而不需要看内容&#xff0c;这个时候把它转换成acc更加节省存储空间&#xff0c;变成了音频带上耳机听就可以了。 那么视频怎么转音…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑩

单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应处理&#xff0c;程序根据两个输入参数iRecordNum和IType计算x的值并返回。编写程序代码&#xff0c;使用JUnit框架编写测试类对编写的程序代码进行测试&#xff0c;测试类中设计最少的测试数据满足基路…

yolov8n 瑞芯微RKNN和地平线Horizon芯片仿真测试部署,部署工程难度小、模型推理速度快

特别说明&#xff1a;参考官方开源的yolov8代码、瑞芯微官方文档、地平线的官方文档&#xff0c;如有侵权告知删&#xff0c;谢谢。 模型和完整仿真测试代码&#xff0c;放在github上参考链接 模型和代码。 因为之前写了几篇yolov8模型部署的博文&#xff0c;存在两个问题&…

全网首发!Yolov8_obb旋转框检测(DOTA1.0数据集)

一、YOLOv8环境搭建 &#xff08;1&#xff09;Pytorch的安装 如果你的环境没有部署请参考本人文章&#xff1a;NLP笔记&#xff08;2&#xff09;——PyTorch的详细安装_安装torchnlp-CSDN博客 &#xff08;2&#xff09;下载最新的Yolov8-obb代码&#xff1a; https://git…

909. 蛇梯棋(图的BFS)

使用广度优先搜索来遍历从1到n*n的可能的路径。 这道题可以看作一个有向图&#xff0c;每个值为x的节点指向x1点节点&#xff0c;在蛇桥处&#xff0c;是从x指向y 注意这里的x、x1、y都是值&#xff0c;可以根据值计算出对应的行列值&#xff0c;计算规则&#xff1a; 假设值…

Redis相关知识点

1.什么是Redis Redis (REmote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值对&#xff08;key-value&#xff09;数据库 特征&#xff1a; 1.数据间没有必然的关联关系 2.内部采用单线程机制进行工作 3.高性能&#xff0c;官方提供测试数据&#xff0c;50个…

【修图】AI修图工具

人脸替换 免费的人脸替换工具&#xff1a; Face Swap&#xff1a; https://vmodel.ai/face-swap 支持单人换脸、多人换脸 AI消除 SnapEdit https://snapedit.app/remove-object 不付费的话只能下载清晰度较低的版本 但我试了几个在线的AI消除工具&#xff0c;SnapEdit算是…