【问题标题】:How to initialize a map from a set如何从集合中初始化地图
【发布时间】:2016-10-01 00:41:18
【问题描述】:

我想使用std::set(或std::vector)的元素设置std::map 的键。

类似下面的...

std::set<int> keys = { 3,4,6 };
std::map<int,string> results(keys); // syntax error

这可以在不显式迭代集合的情况下完成吗?

【问题讨论】:

  • 1) 你有什么特别的原因吗? 2) 你希望值字符串是什么?
  • 如果这是可能的,我会感到非常惊讶,因为访问该语句产生的未初始化值不应该是允许的行为。
  • @Beta: 1) 我想创建一个 API,允许简洁地指定键(不考虑值)。 2)我希望该值被默认初始化。
  • 但是为什么你不想明确地迭代集合呢?除非您有特定原因,否则我认为这更多是关于编码风格的问题。
  • @Brian:是的,它本质上是为了风格。我只是想知道是否有一种非常简洁的方法来完成分配。基于the similarities between the two containers,这似乎是一件很自然的事情。

标签: c++ stdmap stdset


【解决方案1】:

你不能。 map 不是 set。它们是根本不同的容器,即使底层结构相似。


也就是说,一切皆有可能。如果范围的元素已经排序,std::map 的范围构造函数是线性时间,set 为我们保证。因此,您需要做的就是将变压器应用于每个元素以产生新的范围。最简单的方法是使用 boost::make_transform_iterator 之类的东西(或自己动手):

template <class K, class F
    class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type>
std::map<K, V> as_map(std::set<K> const& s, F&& f) {
    return std::map<K,V>(
        boost::make_transform_iterator(s.begin(), f),
        boost::make_transform_iterator(s.end(), f));
}

std::map<int,string> results =
    as_map(keys, [](int i){
        return std::make_pair(i, std::string{});
    });

如果你总是想要默认初始化,可以简化为:

template <class V, class K>
std::map<K, V> as_map_default(std::set<K> const& s) {
    auto f = [](K const& k) { return std::make_pair(k, V{}); }
    return std::map<K,V>(
        boost::make_transform_iterator(s.begin(), f),
        boost::make_transform_iterator(s.end(), f)); 
}

std::map<int,string> results = as_map_default<string>(keys);

【讨论】:

  • 每当有人说“最简单的解决方案就是 X”,而 X 涉及到定义 class V 的那一行时,就会有人发誓永远不会学习 C++。 :-) 注意:我喜欢这个答案,但是伙计,这么多尖括号。
  • 答案从“你不能”开始,然后继续演示如何去做。迭代隐藏在地图构造函数中,这基本上是我希望看到的。所以这是正确的答案,除了前两个词。 :)
【解决方案2】:

这可以在不显式迭代集合的情况下完成吗?

没有。如果不对其进行迭代,就无法知道集合中的键。您可以编写函数使其显示,就好像存在隐式转换一样,但这些函数最终必须迭代源集合。

一个简单的方法如下:

#include <set>
#include <map>
#include <string>

auto build_map(const std::set<int>& source) -> std::map<int,std::string>
{
  std::map<int,std::string> results;
  for (auto const& i : source) {
    results[i];
  }
  return results;
}

int main()
{
  std::set<int> keys = { 3,4,6 };
  auto results = build_map(keys);
}

当然,如果这样可以提高可读性,我们可以进行模板化:

#include <set>
#include <vector>
#include <unordered_set>
#include <map>
#include <string>
#include <utility>

template<class MappedType, class SourceContainer>
auto build_map(SourceContainer&& source)
{
  using source_type = std::decay_t<SourceContainer>;
  using key_type = typename source_type::value_type;

  std::map<key_type , MappedType> results;
  for (auto const& i : source) {
    results[i];
  }
  return results;
}

int main()
{
  std::set<int> keys = { 3,4,6 };
  auto results = build_map<std::string>(keys);

  // also
  results = build_map<std::string>(std::vector<int>{3, 4, 6});
  results = build_map<std::string>(std::unordered_set<int>{3, 4, 6});
}

【讨论】:

  • 一个很好的例子,说明如何非常简单地进行基于迭代的初始化。
猜你喜欢
  • 2020-05-11
  • 2018-10-05
  • 1970-01-01
  • 2017-08-19
  • 2017-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多