How can I set two kind of comparator (one for insert, one for find) on this multiset?

后端 未结 4 1726
忘了有多久
忘了有多久 2021-01-23 14:48

I have declared this STL multiset:

multiset playingNotes;

and my comparator is:



        
相关标签:
4条回答
  • 2021-01-23 14:54

    Use std::find_if.

    int value = 60;
    auto iteratorItemFound = std::find_if(std::begin(playingNotes), std::end(playingNotes), [value](const IMidiMsgExt& msg)
    {
        return msg.mNote == value;
    });
    
    0 讨论(0)
  • 2021-01-23 15:00

    I'd agree with Mohamad Elghawi's answer. But it is incomplete.

    Your actual code will use a find_if, just like this:

    const auto it = find_if(cbegin(playingNotes), cend(playingNotes), [value = int{60}](const auto& i){return i.mNote == value;});
    
    if(it != cend(playingNotes)) {
        playingNotes.erase(it);
    }
    

    This will remove the IMidiMsgExt with the lowest mTick value which has an mNote of 60 (or whatever value was initialized to.) If there are multiple IMidiMsgExts in playingNotes that tie for the lowest, the IMidiMsgExt that has been in playingNotes the longest will be removed.

    You're explanation of the problem was a little sparse, so I've created and solved a Minimal, Complete, Verifiable Example here: http://ideone.com/oFQ4rS

    0 讨论(0)
  • 2021-01-23 15:04

    What you want to do is not possible (while keeping the logarithmic search properties of the map). The reason is that the find method takes an instance of key_type, so even writing a custom comparator which has overloads for comparing your key_type against ints will not work.

    The idea of a key_type is that it's an immutable and cheap object to construct - i.e. you should be unafraid to treat it as a value that can be copied.

    Edit:

    Here's how I may approach it.

    Synopsis:

    1. use a sorted vector (sorted by timestamp) as my 'set'.
    2. this allows me to search in logarithmic time, same as a set
    3. but I can now search across the set quickly (no pointer de-referencing and good cache locality)

    code:

    #include <vector>
    #include <algorithm>
    
    struct midi_message
    {
      midi_message(int timestamp, int note) 
        : _timestamp(timestamp)
        , _note(note)
        {}
      int timestamp() const { return _timestamp; }
      int note() const { return _note; }
    private:
      int _timestamp, _note;
    };
    
    struct earlier
    {
      bool operator()(const midi_message& l, const midi_message& r) const {  
        return l.timestamp() < r.timestamp();
      }
    
      bool operator()(const midi_message& l, const int& r) const {  
        return l.timestamp() < r;
      }
    };
    
    struct midi_messages
    {
      // insert messages, keeping the map sorted by timestamp
      void add(midi_message m) {
        auto i = std::lower_bound(std::begin(_data),
                                      std::end(_data),
                                      m.timestamp(),
                                      earlier());
    
        _data.insert(i, std::move(m));
      }
    
      bool remove_first_note_like(int note)
      {
        auto i = std::find_if(std::begin(_data), 
                              std::end(_data),
                              [note](auto& msg) 
                              { return msg.note() == note; });
        if (i != std::end(_data)) {
          _data.erase(i);
          return true;
        }
        return false;
      }
    
      std::size_t remove_all_before(int timestamp)
      {
        auto new_begin = std::lower_bound(std::begin(_data),
                                          std::end(_data),
                                          timestamp,
                                          [](auto& msg, auto& timestamp) {
                                            return msg.timestamp() < timestamp;
                                          });
        _data.erase(std::begin(_data), new_begin);
      }
    
      private:
      std::vector<midi_message> _data;
    };
    
    int main()
    {
      midi_messages messages;
    
      messages.add(midi_message(1000, 60));
      messages.add(midi_message(1000, 60));
      messages.add(midi_message(1000, 60));
      messages.add(midi_message(1001, 60));
      messages.add(midi_message(1002, 70));
      messages.add(midi_message(1002, 60));
      messages.remove_first_note_like(60);
      messages.remove_all_before(1001);
    }
    
    0 讨论(0)
  • 2021-01-23 15:08

    Solution with Boost.MultiIndex:

    Live Coliru Demo

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/member.hpp>
    
    using namespace boost::multi_index;
    
    struct IMidiMsgExt
    {
      int mTick;
      int mTone;
    };
    
    using MidiSet=multi_index_container<
      IMidiMsgExt,
      indexed_by<
        ordered_unique<member<IMidiMsgExt,int,&IMidiMsgExt::mTick>>,
        hashed_non_unique<member<IMidiMsgExt,int,&IMidiMsgExt::mTone>>
      >
    >;
    
    #include <iostream>
    
    int main()
    {
      MidiSet m={{0,100},{2,60},{3,150},{5,60},{1,200},{4,90}};
    
      std::cout<<"before erasing:\n";
      for(const auto& msg:m)std::cout<<"["<<msg.mTick<<","<<msg.mTone<<"]";
      std::cout<<"\n";
    
      m.get<1>().erase(60);
    
      std::cout<<"after erasing:\n";
      for(const auto& msg:m)std::cout<<"["<<msg.mTick<<","<<msg.mTone<<"]";
      std::cout<<"\n";
    }
    

    Output

    before erasing:
    [0,100][1,200][2,60][3,150][4,90][5,60]
    after erasing:
    [0,100][1,200][3,150][4,90]
    
    0 讨论(0)
提交回复
热议问题