Dun Peal
Dun Peal

Reputation: 17679

How to treat slightly different types as the same in C++?

I have two priority_queues - a minheap and a maxheap:

priority_queue<int> maxheap;
priority_queue<int, vector<int>, greater<int>> minheap;

Later in the code, I need to pop() from whichever heap is larger:

(maxheap.size() > minheap.size() ? maxheap : minheap).pop()

This line generates an error:

error: incompatible operand types ('priority_queue<[2 * ...], (default) std::less<int>>' and 'priority_queue<[2 * ...], greater<int>>')

Apparently due to the different comparator making maxheap and minheap's type different.

Is there a way around this? Clearly these two types are highly compatible, so it's very useful to be able to treat them as the same.

Upvotes: 1

Views: 100

Answers (1)

Vittorio Romeo
Vittorio Romeo

Reputation: 93264

Using the ternary operator requires both objects to have a common type, which is not the case because the two types you mentioned are not related (neither by inheritance, nor by being convertible to one another).

Just change your code to:

if (maxheap.size() > minheap.size())
{
    maxheap.pop();
}
else
{
    minheap.pop();
}

Clearly these two types are highly compatible

These two types satisfy a simple concept of exposing a .pop() method, but they're not related.

so it's very useful to be able to treat them as the same

Templates and concepts can help you with that, but there's no shorthand notation for a tiny template that behaves like a ternary operator. The best you can get is:

pick(a, b, [](const auto& a, const auto& b)
{
    return a.size() > b.size();
},
[](auto&& x)
{
    x.pop()
});

Where pick is something like (rough, needs forwarding and untested):

template <typename A, typename B, typename Predicate, typename F>
void pick(A&& a, B&& b, Predicate&& predicate, F&& f)
{
    if (predicate(a, b))
    {
        f(a);
    }
    else 
    {
        f(b);
    }
}

But the amount of boilerplate defeats the purpose.

Upvotes: 11

Related Questions