11

Consider following comparison function:

bool compare(std::shared_ptr<myObject> &lhs, std::shared_ptr<myObject> &rhs){
   return lhs->value < rhs->value;
}

Now idea is to initialize a multiset of type std::shared_ptr<myObject> which orders elements with above function. So from book i read it should be done like this:

std::multiset<std::shared_ptr<myObject>, decltype(compare)*> myset{compare};

QUESTION:

My question is, in the declaration i understand a function pointer is passed to refer to compare function, but why are we initializing the set with {compare}? What is its importance and why is it necessary to do so like this??

rawrex
  • 4,044
  • 2
  • 8
  • 24
Jason
  • 147
  • 1
  • 1
  • 7

4 Answers4

14

Because the set needs a comparison functor to work with. If you don't specify one, it will make a default-constructed one. In this case, since you're using a function-pointer type, the default-constructed one will be a null pointer, which can't be called; so instead, you have to provide the correct function pointer at run time.

A better approach might be to use a function class type (a.k.a. functor type); then the function call can be resolved at compile time, and a default-constructed object will do the right thing:

struct compare {
    bool operator()(std::shared_ptr<myObject> &lhs, 
                    std::shared_ptr<myObject> &rhs) const {
        return lhs->value < rhs->value;
    }
};

std::multiset<std::shared_ptr<myObject>, compare> myset;
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • thanks, after reading jrok comment/answer now i again read your answer and understood your point, earlier i didn't understood (: – Jason Sep 10 '13 at 12:22
3

decltype(compare)* in the template parameter specifies the type of the comparator. It doesn't tell which function is to be used - whether is it compare, foo, bar or something else. Hence the constructor parameter.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • ins't decltype(compare) refers to type and when we add (*) the whole expression refers to function pointer???? – Jason Sep 10 '13 at 12:09
  • The parameter is a type - in your case it's the same as `bool(*)(shared_ptr&, shared_ptr&)`. That doesn't tell anything about which function it is. – jrok Sep 10 '13 at 12:11
  • Ok it makes sense but im still confused as to why the actual function needs to be placed in initializing block, to me it is just complex syntax or maybe i still need to study templates? – Jason Sep 10 '13 at 12:19
  • @Jason `decltype` queries the type of an expression at compile time while a function pointer itself is an object present at runtime. Perhaps you indeed need to study templates - look up the difference between type and non-type template parameters. – jrok Sep 10 '13 at 12:24
3

In order to access your elements, you need to provide function for strict weak ordering for your type.

std::multiset have the following constructor:

 explicit multiset (const key_compare& comp = key_compare(),
               const allocator_type& alloc = allocator_type());

As you can see, you can do this by passing comp function pointer (or function object) to the constructor.


Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
1

The comparator passed to the template has to be the type of something that can be called with the function call operator. That would be either a class that has overloaded that operator, or the type of a lambda or a function pointer. In the cunstrutor of set, an instance of that type has to be passed. So decltype(compare)* is the function pointer type, and &compare is the function pointer.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • but if we are strictly always calling the same function and referring to its type, why we need to specify these separately? can it be that i can refer to type and call a different funtion/lamda etc – Jason Sep 10 '13 at 12:21
  • A different lambda: no, because another lambda would have another type. A different function: yes, if it has the same type. That means, you could have two sets of the same type, but the comparator works differently, e.g. with `lhs->value > rhs->value`. The type has to be specified separately, because that's the way templates work - in functions, the template argument may be deduced, but `std::set` is a class, so its not possible there. – Arne Mertz Sep 10 '13 at 16:15