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

news2025/4/16 15:21:09

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;
        else
            std::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;
        else
            std::cout << "小幅遮球" << std::endl;
    }
};

int main()
{
    // const reference to int
    int i = 42;
    const int& r1 = i; // we can bind a const int& to a plain int object
    const int& r2 = 42; // 42 is an expression, whose type is int
    const int& r3 = r1 * 2; // r1 * 2 is an expression, whose type is int
    const int& r4 = 3.14;
    const int& r5 = sizeof(int);

    // const reference to string
    const std::string s1 = "hello";
    const std::string& s2 = "hello";

    // const reference to std::function
    const std::function<void(int)> f1 = echo;
    f1(42);

    // labmda -> std::function
    const std::function<void(int)>& f2 = [](int m) -> void {
        std::cout << "m is " << m << std::endl;
    };
    f2(233);

    // functor -> std::function
    Wangchuqin wcq;
    const std::function<void(int)>& f3 = wcq;
    f3(2025);

    // const reference to pointer
    const 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_VER
    std::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)>]

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

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

相关文章

Ubuntu2404装机指南

因为原来的2204升级到2404后直接嘎了&#xff0c;于是要重新装一下Ubuntu2404 Ubuntu系统下载 | Ubuntuhttps://cn.ubuntu.com/download我使用的是balenaEtcher将iso文件烧录进U盘后&#xff0c;使用u盘安装&#xff0c;默认选的英文版本&#xff0c; 安装后&#xff0c;安装…

Spring Cloud初探之使用load balance包做负载均衡(三)

一、背景说明 基于前一篇文章《Spring Cloud初探之nacos服务注册管理(二)》&#xff0c;我们已经将服务注册到nacos。接下来继续分析如何用Spring cloud的load balance做负载均衡。 load balance是客户端负载均衡组件。本质是调用方拿到所有注册的服务实例列表&#xff0c;然…

vector常用的接口和底层

一.vector的构造函数 我们都是只讲常用的。 这四个都是比较常用的。 第一个简单来看就是无参构造&#xff0c;是通过一个无参的对象来对我们的对象进行初始化的&#xff0c;第一个我们常用来当无参构造来使用。 第二个我们常用的就是通过多个相同的数字来初始化一个vector。 像…

【2025年3月中科院1区SCI】Rating entropy等级熵及5种多尺度,特征提取、故障诊断新方法!

引言 2025年3月&#xff0c;研究者在国际机械领域顶级期刊《Mechanical Systems and Signal Processing》&#xff08;JCR 1区&#xff0c;中科院1区 Top&#xff0c;IF&#xff1a;7.9&#xff09;上以“Rating entropy and its multivariate version”为题发表科学研究成果。…

【AI学习】李宏毅老师讲AI Agent摘要

在b站听了李宏毅2025最新的AI Agent教程&#xff0c;简单易懂&#xff0c;而且紧跟发展&#xff0c;有大量最新的研究进展。 教程中引用了大量论文&#xff0c;为了方便将来阅读相关论文&#xff0c;进一步深入理解&#xff0c;做了截屏纪录。 同时也做一下分享。 根据经验调整…

Nacos-Controller 2.0:使用 Nacos 高效管理你的 K8s 配置

作者&#xff1a;濯光、翼严 Kubernetes 配置管理的局限 目前&#xff0c;在 Kubernetes 集群中&#xff0c;配置管理主要通过 ConfigMap 和 Secret 来实现。这两种资源允许用户将配置信息通过环境变量或者文件等方式&#xff0c;注入到 Pod 中。尽管 Kubernetes 提供了这些强…

【BUG】Redis RDB快照持久化及写操作禁止问题排查与解决

1 问题描述 在使用Redis 的过程中&#xff0c;遇到如下报错&#xff0c;错误信息是 “MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk...”&#xff0c;记录下问题排查过程。 2 问题排查与解决 该错误提示表明&#…

java分页实例

引言 在现代Web应用和移动应用中&#xff0c;面对大量数据的展示&#xff0c;分页技术成为了提升用户体验和优化数据加载效率的关键手段。尤其是在MySQL数据库环境中&#xff0c;合理运用分页查询不仅能显著减少服务器负载&#xff0c;还能提升数据访问速度&#xff0c;为用户提…

【Linux篇】ELF文件及其加载与动态链接机制

ELF文件及其加载与动态链接机制 一. EFL文件1.1 ELF文件结构二. ELF文件形成与加载2.1 ELF形成可执行2.2 ELF控制性文件的加载2.2.1总结 三. ELF加载与进程地址空间3.1 动态链接与动态库加载3.1.1 进程如何看到动态库 3.2 全局偏移量表GOT(global offset table&#xff09;3.2.…

经典算法 判断一个图中是否有环

判断一个图中是否有环 问题描述 给一个以0 0结尾的整数对列表&#xff0c;除0 0外的每两个整数表示一条连接了这两个节点的边。假设节点编号不超过100000大于0。你只要判断由这些节点和边构成的图中是否存在环。存在输出YES&#xff0c;不存在输出NO。 输入样例1 6 8 5 3 …

AI与深度伪造技术:如何识别和防范AI生成的假视频和假音频?

引言&#xff1a;深度伪造的崛起 近年来&#xff0c;人工智能技术迅猛发展&#xff0c;其中深度伪造&#xff08;Deepfake&#xff09; 技术尤为引人注目。这项技术利用深度学习和神经网络&#xff0c;可以轻松生成高度逼真的假视频和假音频&#xff0c;使人物的面部表情、语音…

ESP32驱动读取ADXL345三轴加速度传感器实时数据

ESP32读取ADXL345三轴加速度传感器实时数据 ADXL345三轴加速度传感器简介ADXL345模块原理图与引脚说明ESP32读取ADXL345程序实验结果 ADXL345三轴加速度传感器简介 ADXL345是一款由Analog Devices公司推出的三轴数字加速度计&#xff0c;分辨率高(13位)&#xff0c;测量范围达…

【Linux】系统入门

【Linux】系统初识 起源开源 闭源版本内核内核编号 Linux的安装双系统(不推荐)WindowsLinuxvmware虚拟机vitualbox操作系统的镜像centos 7/ubuntu云服务器租用 Linux的操作lsmkdir 文件名pwdadduser userdel -rrm文件名cat /proc/cpuinfolinux支持编程vim code.c./a.out 运行程…

github配置ssh,全程CV

1)随便找一个文件夹右键进入git bash 2)验证是否已有公私钥文件 cd ~/.ssh ls如果不存在则生成然后获取 生成时一直回车 ssh-keygen -t rsa -C "xxxxxx.com" cd ~/.ssh cat id_rsa.pub如果存在则直接获取 cd ~/.ssh cat id_rsa.pub3&#xff09;复制 4&#xf…

Dify简介:从架构到部署与应用解析

Dify 是一个开源的生成式 AI 应用开发平台&#xff0c;融合了后端即服务&#xff08;Backend as a Service, BaaS&#xff09;和 LLMOps 的理念&#xff0c;旨在帮助开发者快速搭建生产级的生成式 AI 应用。本文将详细解析 Dify 的技术架构、部署流程以及实际应用场景&#xff…

碳化硅(SiC)功率模块方案对工商业储能变流器PCS市场格局的重构

碳化硅&#xff08;SiC&#xff09;模块方案&#xff08;如BMF240R12E2G3&#xff09;对工商业储能变流器PCS市场格局产生颠覆性的重构&#xff1a; 2025年&#xff0c;SiC模块方案&#xff08;如BMF240R12E2G3&#xff09;凭借效率、成本和政策支持的三重优势&#xff0c;将重…

Redis入门(Java中操作Redis)

目录 一 基础概念 1. Redis 核心特点 2. Redis 与 MySQL 的对比 3. Redis的开启与使用 二 Redis的常用数据类型 1 基础概念 2 数据结构的特点 三 Redis基础操作命令 1 字符串操作命令 2 哈希操作命令 3 列表操作命令 4 集合操作命令 5 有序集合操作命令 6 通用命令…

算法思想之位运算(一)

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;算法思想之位运算(一) 发布时间&#xff1a;2025.4.12 隶属专栏&#xff1a;算法 目录 算法介绍六大基础位运算符常用模板总结 例题位1的个数题目链接题目描述算法思路代码实现 比特位计数题目链接题目描述算法思路…

【基于Servlet技术处理表单】

文章目录 一、实验背景与目的二、实验设计与实现思路1. 功能架构2. 核心代码实现3. 测试用例 总结 一、实验背景与目的 本次实验旨在深入理解Servlet工作原理&#xff0c;掌握JSP与Servlet的协同开发&#xff0c;实现前端表单与后端数据处理的交互。具体目标包括&#xff1a;设…

[OS] mmap | fd是什么 | inode机制 | vfs封装

Linux 下一切皆文件 * 统统抽象为文件&#xff0c;系统封装一层结构体之后&#xff0c;通过指针来访问 * 文章后面的 几个思考题都挺好的 * 后面涉及到的inode 机制&#xff0c;去年暑假的这篇文章&#xff0c;有详细的记录到过 【Linux】(26) 详解磁盘与文件系统&#xff1a;从…