C++: Initialization and References to const 初始化和常引用

news/2025/5/24 1:34:38

cpp primer 5e, P97.

理解

这是一段很容易被忽略、 但是又非常重要的内容。

In § 2.3.1 (p. 51) we noted that there are two exceptions to the rule that the type of a reference must match the type of the object to which it refers. The first exception is that we can initialize a reference to const from any expression that can be converted (§ 2.1.2, p. 35) to the type of the reference. In particular, we can bind a reference to const to a nonconst object, a literal, or a more general expression:

对于 reference 来说,只能是如下的其中之一:

  • type of a reference must match the type of the object to which it refers. 引用类型,必须和被引用的类型一样
  • the first exception is that, we can initialize a reference to const from any expression that can be converted to the type of reference
    第一个例外: 如果一个表达式能够转为被引用的类型, 那么这个表达式能被用于初始化一个引用
  • 第二个例外,这里暂时没提及, 应该是右值引用

也就是:
情况1:

T a;
const T& b = a;

情况2:

const T& c = <expression>; // <expression> is type of T, or can be converted to type of T

其中情况2,当表达式不是类型T、但是可以转换为类型T,我想到这几种常见的:

// double/float -> int
const int& r4 = 3.14;// const char* -> std::string
const std::string& s2 = "hello";// non-void pointer -> void*
int* data = (int*) malloc(sizeof(int) * 100);
const void* buf = data;// lambda -> std::function
const std::function<void(int)>& f2 = [](int m) -> void {std::cout << "m is " << m << std::endl;
};
f2(233);// functor -> std::function
struct Wangchuqin
{void operator()(int k){if (k % 3 == 0)std::cout << "遮球" << std::endl;elsestd::cout << "小幅遮球" << std::endl;}
};
Wangchuqin wcq;
const std::function<void(int)>& f3 = wcq;
f3(2025);

实践

// P97#include <iostream>
#include <string>
#include <functional>void echo(int n)
{std::cout << "n is " << n << std::endl;
}struct Wangchuqin
{void operator()(int k){if (k % 3 == 0)std::cout << "遮球" << std::endl;elsestd::cout << "小幅遮球" << std::endl;}
};int main()
{// const reference to intint i = 42;const int& r1 = i; // we can bind a const int& to a plain int objectconst int& r2 = 42; // 42 is an expression, whose type is intconst int& r3 = r1 * 2; // r1 * 2 is an expression, whose type is intconst int& r4 = 3.14;const int& r5 = sizeof(int);// const reference to stringconst std::string s1 = "hello";const std::string& s2 = "hello";// const reference to std::functionconst std::function<void(int)> f1 = echo;f1(42);// labmda -> std::functionconst std::function<void(int)>& f2 = [](int m) -> void {std::cout << "m is " << m << std::endl;};f2(233);// functor -> std::functionWangchuqin wcq;const std::function<void(int)>& f3 = wcq;f3(2025);// const reference to pointerconst void* ptr1 = 0;const void* ptr2 = &i;int* data = (int*) malloc(sizeof(int) * 100);const void* buf = data;free(data);return 0;
}

更实际的实践

https://godbolt.org/z/bcrhc77TM

#include <functional>
#include <vector>
#include <algorithm>
#include <iostream>int main() { using MyFunc = std::function<void(int)>;std::vector<const MyFunc *> myFuncs;auto f = [](int v) {std::cout << v << "\n";};auto pushBack = [&myFuncs](const MyFunc & func) {myFuncs.emplace_back(&func);};pushBack(f);auto callIt  = [val = 1] (const MyFunc* func) {func->operator()(val);};for (auto func : myFuncs) {callIt(func);}
}

上述代码会 crash。 如果开启 ASAN 则能发现非法内存访问。为什么呢?
因为 pushBack(f) 这句不简单呢:

  • f 是 lambda
  • fconst MyFunc & func 发生了转换

这个转换,是生成「临时的」 MyFuncs 对象。这个所谓的「临时的」,可以认为是匿名的、编译器生成的。 这样的匿名对象,声明周期是有限的:发生在 pushBack 定义中,参数传递阶段:

auto pushBack = [&myFuncs](const MyFunc & func) { // 这里

而具体的实现中,压入的是临时对象的地址:

myFuncs.emplace_back(&func);

为啥说是临时对象的地址? 因为 引用的本质是 别名(alias); 取引用的地址, 其实,就是取匿名对象的地址。

匿名对象马上就「死了」,你还取地址; 那以后再用这个匿名对象,就是使用「鬼魂」,本质是使用非法内存。 那肯定crash啊。

当然,如果你还是不信,可以让编译器告诉你, lambda 和 std::function 类型是不一样的:

https://godbolt.org/z/PWcPvzYG1

template<typename T>
void show_type() {
#ifdef __GNUC__std::cout << __PRETTY_FUNCTION__ << "\n";
#elif _MSC_VERstd::cout << __FUNCSIG__ << "\n";
#endif
}show_type<decltype(f)>();
show_type<MyFunc>();
➜  defense-in-cpp git:(main)cd 99 git:(main) ✗ g++ bar.cpp -std=c++11
./%                                                                                                                                                            
➜  9 git:(main) ✗ ./a.out 
void show_type() [T = (lambda at bar.cpp:21:14)]
void show_type() [T = std::function<void (int)>]

https://dhexx.cn/news/show-5497586.html

相关文章

Vue 人看 React useRef:它不只是替代 ref

如果你是从 Vue 转到 React 的开发者&#xff0c;初见 useRef 可能会想&#xff1a;这不就是 React 版的 ref 吗&#xff1f;但真相是 —— 它能做的&#xff0c;比你想象得多得多。 &#x1f440; Vue 人初见 useRef 在 Vue 中&#xff0c;ref 是我们访问 DOM 或响应式数据的…

(51单片机)LCD显示日期时间时钟(DS1302时钟模块教学)(LCD1602教程)

目录 源代码 main.c LCD1602.c LCD1602.h DS1302.c DS1302.h 代码解析与教程&#xff1a; LCD1602模块 DS1302模块 效果视频&#xff1a; 源代码 如上图将5个文放在Keli5 中即可&#xff0c;然后烧录在单片机中就行了 烧录软件用的是STC-ISP&#xff0c;不知道怎么安装的…

scikit-learn初探

KFold k交叉验证&#xff0c;k-1个作为训练集&#xff0c;剩下的作为测试集 split split(X, yNone, groupsNone)X&#xff1a; (n_samples, n_features)的矩阵&#xff0c;行数为n_samples&#xff0c;列数为n_features y&#xff1a;(n_samples,)为列向量&#xff0c;表示监…

Bad Request 400

之前一直以为400就是前端代码有问题 这下遇到了&#xff0c;发现是因为前后端不一致 后端代码注意&#xff1a;现在我写的int 前端请求 原因 &#xff1a;前后端不一致 &#x1f4a1; 问题核心&#xff1a;后端 amount 类型是 int&#xff0c;但前端传了小数 237.31

大象机器人推出myCobot 280 RDK X5,携手地瓜机器人共建智能教育机

摘要 大象机器人全新推出轻量级高性能教育机械臂 myCobot 280 RDK X5&#xff0c;该产品集成地瓜机器人 RDK X5 开发者套件&#xff0c;深度整合双方在硬件研发与智能计算领域的技术优势&#xff0c;实现芯片架构、软件算法、硬件结构的全栈自主研发。作为国内教育机器人生态合…

决策树简介

【理解】决策树例子 决策树算法是一种监督学习算法&#xff0c;英文是Decision tree。 决策树思想的来源非常朴素&#xff0c;试想每个人的大脑都有类似于if-else这样的逻辑判断&#xff0c;这其中的if表示的是条件&#xff0c;if之后的else就是一种选择或决策。程序设计中的…

Linux命令+Git命令

Linux命令Git命令 linux查看两个操作系统cd命令的区别操作文件和文件夹vim不同模式保存和退出 Git注意事项git bash常用的快捷键 linux Linux操作系统中&#xff0c;几乎所有的东西都以文件夹或文件形式存在&#xff0c;这些文件夹/文件有一个共同的根目录/。如果我们在某块磁…

千树万树梨花开

2025年4月15日&#xff0c;13~27℃&#xff0c;还好 待办&#xff1a; 融智云考平台《物理》《物理2》~~《地理》《地理1》~~重修试卷 卫健委统考监考&#xff08;2025年4月12日早上7点半&#xff09; 冶金《物理》课程标准 冶金《物理》教案 期中教学检查——自查表材料&#…

windows10 wsl2 安装ubuntu和docker

见 弃用Docker Desktop&#xff1a;在WSL2中玩转Docker之Docker Engine 部署与WSL入门-阿里云开发者社区 如果启动docker时报下面这个错&#xff0c; 那是因为systemctl没有启用 sudo systemctl start docker System has not been booted with systemd as init system (PID 1)…

体系结构论文(七十一):Quantifying the Impact of Data Encoding on DNN Fault Tolerance

Quantifying the Impact of Data Encoding on DNN Fault Tolerance 研究动机 深度神经网络&#xff08;DNN&#xff09;在硬件运行时可能遇到位翻转&#xff08;bit-flip&#xff09;错误&#xff0c;特别是在能效和面积敏感的平台上&#xff08;如边缘设备、移动端&#xff0…