【问题标题】:Is there an elegant possibility to include several sets into a multimap?是否有一种优雅的可能性将多个集合包含到一个多图中?
【发布时间】:2021-04-24 05:33:35
【问题描述】:

我有几套同类型的:

std::set< TDate> spieltagDatum;

我想将它们全部包含在这种类型的多图中:

std::multimap<TDate, int> ereignis;

是否有一种优雅的可能性(可能与 lambda 相关的函数?)将 ONE 集合的所有成员包含到上面的 multimap 中而不使用迭代器机制? (插入时应使用 INT 参数丰富多图对)。

【问题讨论】:

  • 你为什么不喜欢iterator mechanism,我只是构建了一个转换迭代器类,它看起来不错。
  • 我想,我的拼写错误 - 我已经提到过,如果有一个 STD 函数可以像我通过迭代器那样以更优雅的方式完成这项任务。

标签: c++ set multimap


【解决方案1】:

我可以建议使用带有auto 的简化 for 循环而不是迭代器,如下所示。

例如,我使用整数 TDate 来代替我的代码中的 123,您可以使用任何函数来填充 multimap 的值。

Try it online!

#include <map>
#include <set>

int main() {
    using TDate = int;
    std::set<TDate> spieltagDatum = {3, 5, 7};
    std::multimap<TDate, int> ereignis;
    for (auto & e: spieltagDatum)
        ereignis.emplace(e, 123);
}

【讨论】:

    【解决方案2】:

    “不使用迭代器机制”是什么意思? (不要使用迭代器,后果自负)

    正如您所描述的,您所做的是 1)转换(通过丰富)和 2)插入,所以答案是 std::tranform + std::insert

    #include <algorithm> // transform
    #include <cassert>
    #include <map>
    #include <set>
    
    int main() {
        using TDate = int;
        std::set<TDate> spieltagDatum = {3, 5, 7};
        std::set<TDate> ...;
    
        std::multimap<TDate, int> ereignis;
    
        auto enrich = [](auto e){return std::make_pair(e, 123);};
        std::transform(
            begin(spieltagDatum), end(spieltagDatum), 
            std::inserter(ereignis, end(ereignis)),
            enrich
        );
        ... // repeat for other sets if necessary
    
        assert( ereignis.find(5) != ereignis.end() );
        assert( ereignis.find(5)->second == 123 );
    }
    

    https://godbolt.org/z/zzYbKK83d


    基于@prehistoricpenguin 答案的使用库的更具声明性的选项是: (IMO 主要在 C++17 中值得,其中很多模板参数并不是真正需要的)

    #include <cassert>
    #include <map>
    #include <set>
    #include <boost/iterator/transform_iterator.hpp>
    
    int main() {
        using TDate = int;
        std::set<TDate> spieltagDatum = {3, 5, 7};
        
        auto enriched = [](auto it){
            return boost::transform_iterator(it, [](auto e){return std::pair(e, 123);});
        };
    
        std::multimap ereignis(
            enriched(begin(spieltagDatum)),
            enriched(end  (spieltagDatum))
        );
    
        assert( ereignis.find(5) != ereignis.end() );
        assert( ereignis.find(5)->second == 123 );
    }
    

    https://godbolt.org/z/6ajssjjjP

    【讨论】:

    • 谢谢你们的例子!他们都工作。作为最后一个问题,使用 emplace 代替 insert 时,答案 1 是否有性能优势?
    • 谢谢你们的例子!他们都工作。在第二个答案中我不太了解的是,对于丰富变量究竟做了什么?它是一对,第二个参数是123。但是哪个值有这个对的第一个参数呢?
    • @Mauro 不,如果可以的话,插入器会放置。在任何情况下,放置对于关联容器来说都是一个值得商榷的优化,因为如果你插入一个副本,你可能会付出不必要的代价。您可以对这个主题进行自己的研究。
    • @Mauro enrich 不是变量,它是 lambda 形式的变换操作。我介绍它使用与您的问题相同的命名法。您可以直接将其替换为转换的最后一个参数。在所有情况下,该对的第一个参数是集合中的原始值。该操作从值e(在集合中)到“丰富”对(e, 123)(可能插入到多重映射中)。
    • 谢谢你的解释,我现在明白了。我距离 C++ 编程(仅在 ABAP 中编程)还有 10 年的时间。在此期间,C++11 和 C++14 和 C++17 内部发生了很多变化......
    【解决方案3】:

    一个可能的答案是编写一个转换迭代器类,然后我们使用迭代器来构造multimap 实例。

    #include <iostream>
    #include <iterator>
    #include <map>
    #include <set>
    
    template <typename KeyT, typename ValT>
    class ConvertIter
        : public std::iterator<std::forward_iterator_tag, std::pair<KeyT, ValT>> {
      using SetIter = typename std::set<KeyT>::iterator;
    
     public:
      ConvertIter(SetIter itr, ValT v = ValT{}) : _itr(itr), _val(v) {}
    
      bool operator==(const ConvertIter& other) { return other._itr == _itr; }
      bool operator!=(const ConvertIter& other) { return other._itr != _itr; }
    
      std::pair<KeyT, ValT> operator*() const {
        return {*_itr, _val};
      }
      ConvertIter& operator++() {
        ++_itr;
        return *this;
      }
      ConvertIter& operator++(int) {
        ++_itr;
        return *this;
      }
    
     private:
      SetIter _itr;
      ValT _val;
    };
    
    int main() {
      using TDate = int;
      std::set<TDate> spieltagDatum = {3, 5, 7};
      std::multimap<TDate, int> ereignis(
          ConvertIter<TDate, int>(spieltagDatum.begin(), 123),
          ConvertIter<TDate, int>(spieltagDatum.end()));
    
      for (auto [date, val] : ereignis) {
        std::cout << "[" << date << "," << val << "]" << std::endl;
      }
      return 0;
    }
    

    演示: https://godbolt.org/z/cr98f15jq

    【讨论】:

    • 或者(同样的想法)直接使用Boost.Iterator transform_iteratorstd::multimap&lt;TDate, int&gt; ereignis(boost::make_transform_iterator(begin(spieltagDatum), enrich), boost::make_transform_iterator(end (spieltagDatum), enrich));godbolt.org/z/q8195bbvG。不幸的是,这种类型的代码会吓到人们使用迭代器。
    • @alfC 感谢您提供出色的代码! Boost 有很多很棒的实用程序,但并不广为人知,我喜欢它!我花了很多时间阅读文档但仍然没有意识到make_transform_iterator的存在:(
    • 是的,遗憾的是 Boost 文档对专家来说太友好了,而且到处都是。很难找到您真正需要的东西。例如,您知道如何从头开始编写迭代器(一项高级技能)而您不了解 Boost.Iterator,这真是令人惊讶。这说明 Boost 甚至无法接触到专家。我用完整的基于 Boost 的代码扩展了我的答案。
    • @alfC 与 boost 的文档系统没有任何关系,使其在本地机器上统一和可搜索是我想要的,像 Rust 本地文档之类的就很完美了。跨度>
    • @alfC 发现很多有经验的 C++ 程序员不了解 Boost 并重新构建了很多被 Boost 吸收的库,但是代码质量达不到 Boost 的高质量并且难以维护代码库。这是一个遗憾。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 2012-02-29
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 2016-03-14
    相关资源
    最近更新 更多