Как использовать std::reference_wrapper для хранения ссылки в std::unordered_map?

Я пытаюсь научиться использовать std::reference_wrapper и std::ref и варианты их использования.

Вот пример кода, который я написал. Помогите пожалуйста решить ошибку компиляции. И было бы здорово, если бы вы могли подробно объяснить проблему.

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

Я хочу сохранить ссылку на данные. Тем временем данные будут обновляться, это будет похоже на ссылку на грязный бит данных. В конце моей программы мне просто нужно будет обновить эти значения, не повторяя все std::unordered_map. Я не хочу создавать какие-либо копии в этом процессе, чтобы избежать проблем с производительностью, а также избегаю std::shared_ptr, поскольку считаю, что в этом случае лучше использовать необработанный указатель (я знаю, что объект не выходит за пределы области видимости). ).

Есть ли лучший подход?

Вот ошибка:

.\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<Data> по значению, а не по ссылке на значение r.

Eugene 22.07.2024 20:49

Почему ты не делаешь buffer_.insert({key, r}); так, как делал с keyToData_?

Eljay 22.07.2024 20:51

@Юджин, это не работает

Aman Kumar 22.07.2024 21:00

@Элджей, нет причины, я просто не заметил, моя вина

Aman Kumar 22.07.2024 21:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

std::unordered_map::operator[] требует, чтобы mapped_type был конструктивным по умолчанию. Поскольку std::reference_wrapper<Data> (т. е. mapped_type из std::unordered_map<std::string, std::reference_wrapper<Data>> buffer_;) не является конструктивным по умолчанию, вы получаете ошибку компилятора в следующей строке:

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

Ошибка компилятора исчезнет, ​​как только вы воспользуетесь, например, std::unordered_map::emplace, чтобы добавить ключ-значение к buffer_, что вставит новый элемент в контейнер, созданный на месте с помощью предоставленные аргументы, если в контейнере еще не существует элемента с указанным ключом.

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

Смотрите здесь: https://gcc.godbolt.org/z/zT1PG3d18


При этом я бы предпочел передать Data&, чем значение r, в std::reference_wrapper<Data>, потому что getOrCreateData() возвращает Data&, которое является входными данными для addBuffer(). Нет необходимости в дополнительном наборе преобразований между:

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);
  //                            ^^^
}

большое спасибо! это сработало. Кстати, почему существует разница между реализацией вставки и оператором []? оба должны работать одинаково? я не понимал, что нужно, чтобы его можно было конструировать по умолчанию.

Aman Kumar 22.07.2024 20:59

@AmanKumar На ваши вопросы есть прямой ответ здесь: вставка vs emplace vs оператор в карте C++

JeJo 22.07.2024 21:13

Другие вопросы по теме