Why does c choose a primitive type overload match over a "better" matching initializer list?
#include <vector>
void foo([[maybe_unused]] int i) {}
void foo([[maybe_unused]] const std::vector<int>& v) {}
int main() {
foo(0);
foo({1,2,3});
foo({0}); // calls foo(int) and issues a warning,
// rather than what seems like the "better"
// match foo(vector).. why?
}
<source>:10:9: warning: braces around scalar initializer [-Wbraced-scalar-init]
foo({0}); // calls foo(int) and issues a warning,
^~~
Perhaps "surprising" result, since the compiler chooses the option for which it then issues a diagnostic?
Using Clang 14
https://godbolt.org/z/1dscc5hM4
CodePudding user response:
{0}
doesn't have a type, so we need to try and convert it to the parameter types of the overload set. When considering
void foo([[maybe_unused]] const std::vector<int>& v) {}
We need to consult [over.ics.list]/7.2 which states
Otherwise, the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.
so we have a user defined conversion for this conversion sequence.
Looking at
void foo([[maybe_unused]] int i) {}
We find the conversion covered in [over.ics.list]/10.1 which states
if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type;
The element in this case is 0
, which is an integer literal which is an exact match standard conversion
So now we have a user defined conversion vs a standard conversion and that is covered by [over.ics.rank]/2.1
a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
and now we know that the standard conversion is a better conversion and that is why the int
overload is chosen over the std::vector<int>
overload.