Home > Software engineering >  Why does returning an initializer list in a function returning a priority_queue call the vector cons
Why does returning an initializer list in a function returning a priority_queue call the vector cons

Time:02-20

Consider the following code:

std::priority_queue<int> foo() {
    return {14, 12};
}

I would expect foo() to return a priority_queue containing 2 elements: 14 and 12. However, it returns a priority_queue containing 14 copies of 12. I stepped in with gdb and it appears that the vector constructor is being called. Could someone please explain why this is the case? Thanks.

Edit: Also what would be the most idiomatic way of creating a priority_queue with 2 elements (or any number of elements) other than creating an empty one and inserting elements one by one (or is that the best option)?

CodePudding user response:

Creating a priority_queue with two integers like that:

std::priority_queue<int> pq(a, b);

Was till recently treated by most compilers as legal, sending the parameters to the underlying container, thus creating a queue holding a times b. Although the constructor of priority_queue that is used for that is actually expecting two iterators:

std::priority_queue(InputIterator begin, InputIterator end);

Which is constructor #7 or #8 depending on the C version, as listed in https://en.cppreference.com/w/cpp/container/priority_queue/priority_queue

The arguments provided do not meet the InputIterator requirements. And as it seems, most compilers fixed this issue recently and produce compilation error, see: https://godbolt.org/z/Tqxdzc75d

It is interesting to note that the spec [priority.queue] uses the term InputIterator, but without clearly requiring the arguments to obey to InputIterator requirements. Well as cppreference does require that for constructors 7 to 9:

Iterator-pair constructors. These overloads participate in overload resolution only if InputIt satisfies LegacyInputIterator.

It is quite clear that these constructors are meant for iterators, for example for use case like that:

std::vector<int> v(14, 12);
std::priority_queue<int> p(v.begin(), v.end()); // 14 times 12, now legit

Or, if you want two values - 14 and 12:

std::vector<int> v{14, 12};
std::priority_queue<int> p(v.begin(), v.end()); // 14, 12 - also legit

Bottom line: the original code shall fail compilation.

EDIT

It seems that the recent change is based on LWG defect #3522: Missing requirement on InputIterator template parameter for priority_queue constructors - this made the OP's code illegal and added the requirement for the InputIterator parameters to be actually iterators, already reflected in cppreference, as mentioned above.

CodePudding user response:

Ok so I did a bit more investigation. I at first was confused about which priority_queue constructor I was calling since none of them seemed to make sense. Apparently constructor #8 is called which treats the two ints as iterators and calls vector.insert(vector.end(), 14, 12). However, because they are ints, the insert call is overload #3 instead of #4 which inserts n copies of x.

CodePudding user response:

You can see all the possible constructors for std::priority_queue here.

Most of them are based on a pair of iterators.

[Demo]

std::priority_queue<int> foo() {
    std::vector<int> ret{14, 12};
    return std::priority_queue{std::begin(ret), std::end(ret)};
}

Some others accept the underlying container as a parameter.

[Demo]

std::priority_queue<int> foo() {
    return std::priority_queue{std::less<int>{}, std::vector<int>{14, 12}};
}

CodePudding user response:

You can decleare a priority queue in your function body and use push method to add your values and then return it. Something like

foo(){
priority_queue<int> x;
x.push(14);
x.push(12);
return x;

} code here

This also happens if you have not imported queue, as priority_que is not a memeber of the std class.

  •  Tags:  
  • c
  • Related