C++中如何用reference_wrapper在容器里存储引用?

来源:菜鸟站长作者:石川澪头衔:网络博主
导读:本期聚焦于小伙伴创作的《C++中如何用reference_wrapper在容器里存储引用?》,敬请观看详情。把对象引用直接放进vector或map时,编译器往往会报错,因为大多数容器要求元素类型可拷贝赋值,而原生引用既不可重新绑定也不能默认构造。reference_wrapper正是标准库给出的折中方案,它用轻量对象包裹引用,既保留对被绑对象的操作能力,又满足容器对元素的要求。理解它的构造、访问与生命周期约束,才能避免悬空引用和误用拷贝的问题。

在C++标准库里,容器通常要求元素类型是对象类型,能够拷贝构造和赋值。但很多时候我们并不想拷贝大对象,只是想在容器里管理一批已有对象的“别名”。原生引用做不到这一点,而reference_wrapper提供了一个既像引用、又像对象的桥接类型,让我们可以在vector、list、map等容器中安全地存放引用。

C++中如何用reference_wrapper在容器里存储引用?

为什么原生引用不能放进容器

原生引用在C++中不是对象,它没有独立的存储,也不能被重新绑定。以vector为例,当执行扩容、插入或排序操作时,容器需要移动或拷贝元素,这就要求元素类型至少是可拷贝赋值的。而引用一旦绑定就无法更改目标,也没有默认的空状态,因此下面的代码无法通过编译:

#include <vector>

int a = 1, b = 2;
std::vector<int&> ref_vec; // 错误:vector 的元素不能是引用类型
ref_vec.push_back(a);
ref_vec.push_back(b);

除了语法限制,原生引用还会让容器失去很多通用算法的支持。标准库算法往往假设元素可以被赋值和交换,引用类型无法满足这些前提。因此如果确实需要在容器中保存“指向已有变量的把手”,就必须借助wrapper类。

reference_wrapper的基本用法

reference_wrapper定义在<functional>头文件中,模板参数为被引用的类型。它可以通过std::ref或std::cref创建,也可以直接构造。对象本身占用很小,内部通常只保存一个指针,因此放进容器几乎没有额外开销。

#include <iostream>
#include <vector>
#include <functional>

int main() {
    int x = 10, y = 20, z = 30;
    std::vector<std::reference_wrapper<int>> refs;
    refs.push_back(std::ref(x));
    refs.push_back(std::ref(y));
    refs.push_back(z); // 也可以直接传对象,会隐式构造

    for (int& v : refs) {
        v += 1; // 修改会通过引用影响原变量
    }

    std::cout << x << " " << y << " " << z << std::endl; // 输出 11 21 31
    return 0;
}

需要注意,reference_wrapper重载了operator T&,所以在大多数需要原引用类型的语境里能自动转换。例如上面用int&遍历时,不需要手动调用get()。如果确实需要显式取回引用,可以使用get()成员函数,这在模板推导不明确时很有用。

在关联容器和算法中的使用

reference_wrapper同样可以放在map或set中,但要注意它默认的相等和比较行为依赖于被引用对象的比较,而不是指针地址。如果存放的是reference_wrapper<int>,那么map会以int的值为键进行排序和去重。

#include <map>
#include <functional>
#include <iostream>

int main() {
    int a = 5, b = 3, c = 5;
    std::map<std::reference_wrapper<int>, int> m;
    m[a] = 1;
    m[b] = 2;
    m[c] = 3; // c的值和a相同,会覆盖a的条目

    for (auto& kv : m) {
        std::cout << kv.first.get() << ":" << kv.second << std::endl;
    }
    return 0;
}

在排序、查找等算法中,reference_wrapper也能无缝参与,因为它可以隐式转为引用。不过如果容器中存的是reference_wrapper<const int>,则只能读取不能修改,这和普通const引用语义一致。使用std::cref可以方便地创建常量版本的wrapper。

生命周期与常见陷阱

reference_wrapper不拥有被引用对象的生命周期。如果原对象被销毁,而容器仍然保留wrapper,随后访问就会产生悬空引用,行为和普通引用失效一样危险。下面的例子展示了错误用法:

#include <vector>
#include <functional>

std::vector<std::reference_wrapper<int>> make_bad() {
    int local = 42;
    std::vector<std::reference_wrapper<int>> v;
    v.push_back(local); // local 在函数返回后销毁
    return v; // 返回的容器里全是悬空引用
}

要避免这类问题,应保证被引用的对象生命周期长于容器,或者容器本身只是短期使用。另一个常见误区是以为reference_wrapper能重新绑定,其实它和引用一样,构造时绑定,之后无法改变目标,只能拷贝出新的wrapper指向别处。

与指针和智能指针的对比

有人会问,为什么不直接用指针?指针可以为空、可以重新指向,灵活性更高,但也更容易出现空指针和所有权不清的问题。reference_wrapper强制在构造时绑定有效对象,语义上更明确:这里就是别名,不是可选资源。

方式可否为空可否重绑语义
原生引用别名,不能进容器
reference_wrapper可进容器的别名
裸指针可能为空的间接访问
shared_ptr共享所有权

如果确实需要可选性或共享所有权,指针和智能指针更合适;如果只是临时组织一批已有对象做批量处理,reference_wrapper更轻量且表达意图清晰。结合标准算法如std::sort、std::for_each,它能写出既高效又易读的容器引用管理代码。

C++reference_wrapper容器存储引用修改时间:2026-08-04 10:57:31

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。