Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing std::vector overload instead of int overload on list with one element

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.

like image 506
vsoftco Avatar asked Mar 10 '23 20:03

vsoftco


2 Answers

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

like image 108
songyuanyao Avatar answered Apr 07 '23 01:04

songyuanyao


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?

like image 39
Adrian Colomitchi Avatar answered Apr 07 '23 03:04

Adrian Colomitchi