Consider the code below:
#include <iostream>
#include <vector>
void f(std::vector<int> v) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
void f(int n) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
int main()
{
f({42}); // the int overload is being picked up
}
Live on Coliru
I was a bit surprised to realize that in this case the int overload is being picked up, i.e. the output of the program is:
void f(int)
with the warning
warning: braces around scalar initializer [-Wbraced-scalar-init] f({42});
Of course this happens only when I pass a 1-element list as an argument, otherwise the std::vector
overload is being picked up.
Why is {42}
treated like a scalar and not like a init-list? Is there any way of forcing the compiler to pick the std::vector
overload (without explicitly constructing std::vector<int>{42}
) even on 1-element lists?
PS: The std::vector
has an init-list constructor
vector(std::initializer_list<T> init, const Allocator& alloc = Allocator());
see (7) from cppreference.
Braced initializer has no type, we can't say {42}
is an int
or std::initializer_list<int>
. When it's used as an argument, special rules for overload resolution will be applied for overloaded function call.
(emphasis mine)
- Otherwise, if the parameter type is not a class and the initializer list has one element, the implicit conversion sequence is the one required to convert the element to the parameter type
{42}
has only one element with type int
, then it's exact match for the overload void f(int)
. While for void f(std::vector<int>)
a user-defined conversion is needed. So void f(int)
will be picked up here.
Is there any way of forcing the compiler to pick the std::vector
overload (without explicitly constructing std::vector<int>{42})
even on 1-element lists?
As a wordaround, you can put additional braces to force the compiler construct a std::initializer_list<int>
and then pick up void f(std::vector<int>)
:
f({{42}});
LIVE
Forcing std::vector overload
int main()
{
f(std::vector<int>{42}); // the vector overload is being picked up now
}
Why isn't the vector(initializer_list)
constructor being picked up?
Assume that another header declares a void f(std::set<int> v)
.
How would you like the compiler to react when faced with f({1})
: construct a vector
or construct a set
?