1

I'm trying to learn the use of std::reference_wrapper and std::ref and the use cases of it.

Here is a sample code I wrote. Please help me solve the compilation error. And it would be great if you can explain the issue in depth.

#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));
}

I want to store the reference of the data. The data will keep updating in the meantime, it will be like the dirty bit reference of the data. At the end of my program, I would just need to update these values without iterating the whole std::unordered_map. I don't want to create any copy in this process to avoid performance issues, and I'm also avoiding std::shared_ptr as I think using a raw pointer would be just better in this case (I know that the object is not going out of scope).

Is there any better approach?

Here's the error:

.\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

I have tried changing the function declaration like this:

void addBuffer(const std::string &key, Data &r)
{
  buffer_[key] = std::ref(r);
}
4
  • Pass std::reference_wrapper<Data> by value rather than by r-value reference. Commented Jul 22, 2024 at 18:49
  • Why aren't you doing buffer_.insert({key, r}); like you did with keyToData_? Commented Jul 22, 2024 at 18:51
  • @Eugene that doesn't work Commented Jul 22, 2024 at 19:00
  • @Eljay no reason, i just didn't notice, my bad Commented Jul 22, 2024 at 19:01

1 Answer 1

3

std::unordered_map::operator[] requires the mapped_type to be default constructible. Since std::reference_wrapper<Data> (i.e. the mapped_type of std::unordered_map<std::string, std::reference_wrapper<Data>> buffer_;) is not default constructible, you get the compiler error at the following line:

buffer_[key] = std::ref(r);

The compiler error will go, once you have used, for example, std::unordered_map::emplace to add the key-value to the buffer_, that will insert a new element into the container, constructed in-place using the provided arguments, if no element with the specified key already exists in the container.

buffer_.emplace(key, std::move(r));

See here: https://gcc.godbolt.org/z/zT1PG3d18


That being said, I would rather suggest passing the Data& than the r-value to std::reference_wrapper<Data>, because the getOrCreateData() returns the Data&, which is the input to the addBuffer(). No need of an extra set of conversion in between:

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

...

int main()
{
  // ....
  auto& r1 = dataManager.getOrCreateData("key1");
  // 
  dataManager.addBuffer("key1", r1);
  //                            ^^^
}
Sign up to request clarification or add additional context in comments.

2 Comments

thanks a lot! it worked. btw why is there difference between the implementation of insert and the [] operator? should both just work the same ? i didn't understand the need of it to be default constructable.
@AmanKumar Your ques have direct ans here: insert vs emplace vs operator in c++ map

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.