You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用std::reference_wrapper在std::unordered_map存储引用?编译错误求解

问题:std::reference_wrapper存储到unordered_map的编译错误及优化方案

我正在学习std::reference_wrapper和std::ref的用法及适用场景,编写了如下示例代码,希望存储Data对象的引用(后续数据会持续更新,程序结束时只需更新这些引用指向的值,无需遍历整个std::unordered_map),要求避免拷贝,且因已知对象不会超出作用域,不想用std::shared_ptr,但代码出现编译错误,请求解决并解释原因,同时询问是否有更优实现方案。

示例代码

#include <iostream>
#include <limits>
#include <unordered_map>
#include <functional>

class MyClass
{
public:
  struct Data
  {
    int id;

    Data() : id(std::numeric_limits<int>::max()) {}
  };

  std::unordered_map<std::string, Data> keyToData_;

  Data& getOrCreateData(const std::string &key) 
  {
    auto it = keyToData_.find(key);
    if (it == keyToData_.end())
    {
      it = keyToData_.insert({key, Data()}).first;
    }
    return it->second;
  }

  void addBuffer(const std::string &key, std::reference_wrapper<Data> && r)
  {
    buffer_[key] = r;
  }

private:
  std::unordered_map<std::string, std::reference_wrapper<Data>> buffer_;
};

int main(int, char **argv)
{
  MyClass dataManager{};

  auto &r1 = dataManager.getOrCreateData("key1");
  r1.id = 1;

  dataManager.addBuffer("key1", std::ref(r1));
}

编译错误信息

.\sample.cpp:30:16:   required from here
C:/msys64/mingw64/include/c++/11.3.0/tuple:1824:9: error: no matching function for call to 'std::reference_wrapper<MyClass::Data>::reference_wrapper()'
 1824 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:/msys64/mingw64/include/c++/11.3.0/functional:58,
                 from .\sample.cpp:4:
C:/msys64/mingw64/include/c++/11.3.0/bits/refwrap.h:321:9: note: candidate: 'template<class _Up, class, class> std::reference_wrapper<_Tp>::reference_wrapper(_Up&&) [with _Up = _Up; <template-parameter-2-2> = <template-parameter-1-2>; <template-parameter-2-3> = <template-parameter-1-3>; _Tp = MyClass::Data]'
  321 |         reference_wrapper(_Up&& __uref)
      |         ^~~~~~~~~~~~~~~~~
C:/msys64/mingw64/include/c++/11.3.0/bits/refwrap.h:321:9: note:   template argument deduction/substitution failed:
In file included from C:/msys64/mingw64/include/c++/11.3.0/bits/hashtable_policy.h:34,
                 from C:/msys64/mingw64/include/c++/11.3.0/bits/hashtable.h:35,
                 from C:/msys64/mingw64/include/c++/11.3.0/unordered_map:46,
                 from .\sample.cpp:3:
C:/msys64/mingw64/include/c++/11.3.0/tuple:1824:9: note:   candidate expects 1 argument, 0 provided
  1824 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:/msys64/mingw64/include/c++/11.3.0/functional:58,
                 from .\sample.cpp:4:
C:/msys64/mingw64/include/c++/11.3.0/bits/refwrap.h:326:7: note: candidate: 'constexpr std::reference_wrapper<_Tp>::reference_wrapper(const std::reference_wrapper<_Tp>&) [with _Tp = MyClass::Data]'
  326 |       reference_wrapper(const reference_wrapper&) = default;
      |       ^~~~~~~~~~~~~~~~~
C:/msys64/mingw64/include/c++/11.3.0/bits/refwrap.h:326:7: note:   candidate expects 1 argument, 0 provided

我曾尝试修改函数声明如下,但仍未解决问题:

void addBuffer(const std::string &key, Data &r)
{
  buffer_[key] = std::ref(r);
}

错误原因分析

核心问题在于std::reference_wrapper没有默认构造函数,而std::unordered_map的operator[]在键不存在时,会默认构造对应的值类型对象。当你执行buffer_[key] = r时,如果key不在buffer_中,operator[]会尝试默认构造一个std::reference_wrapper<Data>,但这是不允许的——因为std::reference_wrapper必须绑定到一个已存在的对象,无法默认构造空的引用包装。

解决方法

要避免operator[]的默认构造行为,改用unordered_map的insert或emplace方法,直接构造键值对:

修改后的addBuffer函数

void addBuffer(const std::string &key, std::reference_wrapper<Data> r)
{
    // 直接插入键值对,避免默认构造
    buffer_.insert({key, r});
    // 或者用emplace更高效,避免拷贝pair
    // buffer_.emplace(key, r);
}

或者接受普通引用参数,内部用std::ref包装后插入:

void addBuffer(const std::string &key, Data &r)
{
    buffer_.emplace(key, std::ref(r));
}

这样就不会触发std::reference_wrapper的默认构造,编译即可通过。

更优实现方案

你的需求是跟踪需要更新的Data对象(脏位引用),除了std::reference_wrapper,还有以下几种更贴合场景的方案:

1. 存储原始指针

既然已经确定对象不会提前销毁,直接存储Data*是最简单的方案,语法更直观,性能和reference_wrapper完全一致:

private:
    std::unordered_map<std::string, Data*> buffer_;

// addBuffer修改为:
void addBuffer(const std::string &key, Data &r)
{
    buffer_[key] = &r;
}

后续访问时直接解指针即可,和reference_wrapper的get()方法效果一样。

2. 使用std::set存储引用/指针(去重)

如果同一个Data对象可能被多次添加到"脏列表",可以用std::set<std::reference_wrapper<Data>>或std::set<Data*>自动去重,避免重复处理:

private:
    std::set<std::reference_wrapper<Data>> dirtySet_;

// 添加时直接插入,自动去重
void markDirty(Data &r)
{
    dirtySet_.insert(std::ref(r));
}

// 处理脏数据
void processDirty()
{
    for (auto &ref : dirtySet_)
    {
        Data &data = ref.get();
        // 更新data逻辑
    }
    dirtySet_.clear();
}

这种方案不需要绑定key,更专注于跟踪需要更新的对象本身。

3. 在Data结构体中添加脏位标记

如果允许修改Data结构体,可以直接在Data里加一个bool isDirty = false标记,需要更新时设置为true,最后遍历整个keyToData_处理标记为true的对象。虽然需要遍历,但实现最简单,且如果脏数据比例较高,遍历的性能开销和维护引用列表的开销差异不大:

struct Data
{
    int id;
    bool isDirty = false;

    Data() : id(std::numeric_limits<int>::max()) {}
};

// 标记脏数据
void markDirty(const std::string &key)
{
    getOrCreateData(key).isDirty = true;
}

// 处理脏数据
void processDirty()
{
    for (auto &[key, data] : keyToData_)
    {
        if (data.isDirty)
        {
            // 更新data逻辑
            data.isDirty = false;
        }
    }
}

内容的提问来源于stack exchange,提问作者Aman Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 15:54:56