【问题标题】:STL container find_or_create()STL 容器 find_or_create()
【发布时间】:2021-07-30 16:41:29
【问题描述】:

是否有某种 STL 工具可以在 STL 容器上执行find_or_create()

例如在 unordered_map 的情况下,我经常发现自己需要检索一些值并在不存在值时创建它。有很多函数可以有条件地插入到地图中,但所有这些函数都会立即创建一个新值,即使地图中已经存在一个值:

#include <iostream>
#include <unordered_map>

struct A {
        A() = default;

        A(std::string_view method) {
                std::cout << "Created via " << method << std::endl;
        }
};

int main() {
        std::unordered_map<int, A> m{
                {0, A{}}
        };

        // Get a reference to m[0]; create it if it doesn't exist
        // Key 0 is already in the map, so these calls *should* do little work
        auto &val1 = *m.insert(std::make_pair(0, A{"insert"})).first;
        auto &val2 = *m.emplace(0, A{"emplace"}).first;
        auto &val3 = *m.emplace(std::piecewise_construct,
                        std::make_tuple(0),
                        std::make_tuple("piecewise construct")).first;
        auto &val4 = *m.try_emplace(0, A{"try_emplace"}).first;
        auto &val5 = (m[0] = A{"operator[]"});

        return 0;
}

输出:

Created via insert
Created via emplace
Created via piecewise construct
Created via try_emplace
Created via operator[]

如果对象A 的创建成本很高(需要很长时间来构建、获取系统资源等),则这些不必要的创建可能是不可取的。这经常导致我创建这样的模板:

#include <iostream>
#include <unordered_map>

struct A {
        A() = default;

        A(std::string_view method) {
                std::cout << "Created via " << method << std::endl;
        }
};

template<class Map, class OnCreate>
typename Map::mapped_type &find_or_create(Map &map, const typename Map::key_type &key, const OnCreate &on_create) {
        auto it = map.find(key);
        if (it == map.end())
                it = map.emplace(key, on_create(key)).first;
        return it->second;
}

int main() {
        std::unordered_map<int, A> m;

        // Get a reference to m[0]; create it if it doesn't exist
        auto &val1 = find_or_create(m, 0, [](const int &) { return A{"find_or_create"}; });
        auto &val2 = find_or_create(m, 0, [](const int &) { return A{"never created"}; });
        return 0;
}

输出:

Created via find_or_create

有没有更好(即更简单)的方法通过 STL 实现这一点,或者像这样的模板是可行的方法吗?

【问题讨论】:

  • 把所有的容器都说成是一样的是没有意义的,其实它们不是。选择一个。
  • try_emplace() 不是已经这样做了吗? cppreference 表示,如果映射已经包含该键,则此函数什么都不做,这表明在这种情况下该对象永远不会被构造和销毁。当然,您必须 正确地 使用emplace() 以避免它在内部移动构造实际对象以从您已经构造为emplace() 的参数的对象中插入。

标签: c++ stl c++17


【解决方案1】:

try_emplace 是你想要的。您的问题是您故意构造 A,而不是将参数 传递给构造函数,它可能会或可能不会使用它来构造A

你想要的是这个:

auto &val4 = *m.try_emplace(0, "try_emplace").first;

如果您无法通过构造函数调用创建对象(即:通过工厂函数或其他方式创建实例),您的 find-or-create 函数会很有用。

【讨论】:

  • 是的,这是有道理的,忘记了 try_emplace 是这样工作的!我最近使用 find_or_create 的用例是使用 std::unordered_map> 之类的东西。由于调用 unique_ptr 的构造函数无论如何都需要创建一个 A 实例,我想我会坚持使用 find_or_create 来处理这种情况
猜你喜欢
  • 2011-07-24
  • 2011-02-07
  • 2014-09-09
  • 2011-04-13
  • 2019-05-04
  • 2010-12-06
  • 2013-06-11
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多