0

I need to change the "key" of a multiset:

multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber> playingNotes;

such as that when I use the .find() function it search and return the first object (iterator) with that NoteNumber property value.

I said "first" because my multiset list could contains objects with the same "key". So I did:

struct IMidiMsgExtCompByNoteNumber {
    bool operator()(const IMidiMsgExt& lhs, const IMidiMsgExt& rhs) {
        return lhs.NoteNumber() < rhs.NoteNumber();
    }
};

but when I try to do:

auto it = playingNotes.find(60);

the compiler says no instance of overloaded function "std::multiset<_Kty, _Pr, _Alloc>::find [with _Kty=IMidiMsgExt, _Pr=IMidiMsgExtCompByNoteNumber, _Alloc=std::allocator<IMidiMsgExt>]" matches the argument list

Am I misunderstanding the whole thing? What's wrong?

markzzz
  • 47,390
  • 120
  • 299
  • 507
  • 1
    Can `60` be converted to a `IMidiMsgExt` as that is what [`find`](http://en.cppreference.com/w/cpp/container/multiset/find) expects for an argument? – NathanOliver Apr 28 '16 at 16:56
  • Your other question, http://stackoverflow.com/questions/36918760/how-can-i-set-two-kind-of-comparator-one-for-insert-one-for-find-on-this-mult, seems to have the answer for this question too. No? – R Sahu Apr 28 '16 at 16:58
  • @RSahu: not really! Thats why I've opened this question! – markzzz Apr 28 '16 at 17:03
  • @NathanOliver: so I need to pass a message "similar" to what I'm looking for? Can't find an object for the value of a field? – markzzz Apr 28 '16 at 17:04
  • @paizza Yes. `find` requires a value of type `key`. You need to supply the `key` you are looking for. – NathanOliver Apr 28 '16 at 17:06
  • @NathanOliver: I don't get what You mean. Can you show to me a smart example? – markzzz Apr 28 '16 at 17:07
  • Whats not to get? find needs a `IMidiMsgExt` in order to work. – NathanOliver Apr 28 '16 at 17:10
  • But I don't have any "IMidiMsgExt". I need one from the list that have fixed value in one of its field :O – markzzz Apr 28 '16 at 17:21
  • Or are you saying that I can pass a IMidiMsgExt (even if empty, just NoteNumber filled) with that value, and it will return (from the list) the first that have the same value? – markzzz Apr 28 '16 at 17:26
  • @paizza Yes. You just need to set the value you are looking for. – NathanOliver Apr 28 '16 at 17:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110558/discussion-between-paizza-and-nathanoliver). – markzzz Apr 28 '16 at 17:27

1 Answers1

1

I do believe that you have some misunderstandings here:

  1. Part of an associative container's type is it's key type and comparator. Because C++ is strongly typed the only way to change the comparator on a container is to create a new container, copying or moving all the elements into it
  2. Creating a copy of all the elements in a container is a potentially expensive process
  3. By creating a copy you are violating the Single Source of Truth best practice
  4. multiset is used infrequently, I have used it once in my career, others have pointed out it's shortcomings and recommended that you use another container, write your own container, or in my case I'd suggests simply using vector and sorting it how you want when you have to

I'm going to catalog your comments to show how the answer I've already given you is correct:

  • We're going to assume that the multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber> that you've selected is necessary and cannot be improved upon by using vector as suggested in 4, where:
struct IMidiMsgExtCompByNoteNumber {
    bool operator()(const IMidiMsgExt& lhs, const IMidiMsgExt& rhs) {
        return lhs.NoteNumber() < rhs.NoteNumber();
    }
};
  • You cannot use multiset::find because that requires you tospecify the exact IMidiMsgExt you are searching for; so you'll need to use find_if(cbegin(playingNotes), cend(playingNotes), [value = int{60}](const auto& i){return i.mNote == value;}) to search for a specific property value. Which will be fine to use on to use directly on PlayingNotes without changing the sorting, because you say:

I want to delete the first note that has mNote of 60. No matter the mTime when deleting.

  • You'll need to capture the result of the [find_if], check if it is valid, and if so erase it as demonstrated in my answer, because you say:

The first element find will find for that, erase. [sic]

  • I would roll the code from my answer into a function because you say:

Ill recall find if I want another element, maybe with same value, to get deleted [sic]

Your final solution should be to write a function like this:

bool foo(const multiset<IMidiMsgExt, IMidiMsgExtCompByNoteNumber>& playingNotes, const int value) {
    const auto it = find_if(cbegin(playingNotes), cend(playingNotes), [=](const auto& i){return i.mNote == value;});
    const auto result = it != cend(playingNotes);

    if(result) {
        playingNotes.erase(it);
    }
    return result;
}

And you'd call it something like this: foo(playingNotes, 60) if you wish to know whether an element was removed you may test foo's return.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288