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.
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.
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.