Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort a vector of unique_ptr?

I declared a vector as follows: vector<unique_ptr<Worker>> Workers. Worker is a base class with a private field name and it has two derived classes: Builder and Driver.

I add to the Workers vector objects of Builder and Driver and then I want to sort the vector by name using #include <algorithm> like this:

sort(Workers.begin(), Workers.end(), cmp_by_name);

bool cmp_by_name(const Worker &a, const Worker &b)
{
    return a.getName() < b.getName();
}

But the VS compiler says:

Error 1 error C2664: 'bool (const Worker &,const Worker &)' : cannot convert argument 2 from 'std::unique_ptr>' to 'const Worker &' c:\program files (x86)\microsoft visual studio 12.0\vc\include\algorithm 3071 1 App

How can I fix this error?


Thanks to @NathanOliver, @Rabbid76 and this question, I edited my cmp_by_name into this form:

struct cmp_by_name
{
    inline bool operator()(const unique_ptr<Worker>& a, const unique_ptr<Worker>& b)
    {
        return a->getName() < b->getName();
    }
};

And I call the sort function like this:

sort(Workers.begin(), Workers.end(), cmp_by_name());
like image 755
Da Artagnan Avatar asked Jan 20 '16 17:01

Da Artagnan


3 Answers

The comparison function which std::sort uses needs to be in the form of:

bool cmp(const Type1 &a, const Type2 &b);

Here the types Type1 and Type2 must be such that the iterator can be dereferenced and then implicitly converted to both of them.

In your case dereferencing Workers.begin() gives you a unique_ptr<Worker> not a Worker. You will need to change your comparison function to take a const unique_ptr<Worker>&.

In this case it would wind up looking like:

bool cmp_by_name(const std::unique_ptr<Worker>& a, const std::unique_ptr<Worker>& b)
{
    return a->getName() < b->getName();
}
like image 134
NathanOliver Avatar answered Nov 08 '22 09:11

NathanOliver


The datatype of your std::vector<std::unique_ptr<Worker>> is std::unique_ptr<Worker>, so your comparison function has to look like this:

bool cmp_by_name(const std::unique_ptr<Worker> &a, const std::unique_ptr<Worker> &b)
{
    return a->getName() < b->getName();
}

The comparison function expects to arguments so that an object of the std::vector can convert to them.

like image 45
Rabbid76 Avatar answered Nov 08 '22 10:11

Rabbid76


Since C++11, you can also use a lambda expression instead of defining a comparison function:

int main()
{
    using workers_t = std::unique_ptr<Worker>;
    std::vector<workers_t> Workers;
    Workers.emplace_back(std::make_unique<Worker>(Worker("Paul")));
    Workers.emplace_back(std::make_unique<Worker>(Worker("Anna")));
    Workers.emplace_back(std::make_unique<Worker>(Worker("John")));

    std::sort(std::begin(Workers), std::end(Workers), [](const workers_t& a, const workers_t& b) {
        return a->getName() < b->getName();
    });

    for (auto const &worker : Workers)
        std::cout << worker->getName() << std::endl;

    return 0;
}

Note: This example directly uses Worker objects for the sake of clarity, but it should work for your derived Builder and Driver objects as well.

Code on Ideone

like image 27
honk Avatar answered Nov 08 '22 09:11

honk