Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguious constructors with single argument: initializer_list and int

Tags:

c++

c++11

What determines which constructor should be used when initializing objects using this syntax; is it compiler dependent? (Using VS2015):

Vector v { 3 };

I'm creating my own Vector class (learning C++11) and I have the following:

class Vector
{
public:
    Vector(initializer_list<double> lst);
    Vector(int s);
    // ...
private:
    double* elem;
    int sz;
};

and:

int main()
{
    Vector v1 { 3 };  //calls init_list ctor
    Vector v2 { static_cast<int>(3)}; //calls init_list ctor
    Vector v3 = 3;    //call int ctor
}

My initial thought was using the { 3 } syntax would call the constructor that accepts an int, then realized that it makes sense since I could also use something like { 3, 4, 5, 6 } and that would work--the array passed would been seen as doubles and a Vector would be instantiated.

Then I thought, "how can I use the {}-notation and call the constructor that accepts an int? " I tried casting it to an int so that it would force use of said constructor but that didn't happen. That's when I found out I can use the method used to init v3.

I played around a little bit more and removed the constructor with the initializer_list<double> argument, and to my surprise, initializing v1 and v2 was calling the constructor that takes an int for an argument.

So, is this normal/standard? Is it considered a feature? I would think the int constructor would have precedence when passing a single int to the initializer vs. an implicit cast to double and double*.

like image 996
Kcvin Avatar asked Dec 22 '25 02:12

Kcvin


1 Answers

This is standard. If there is a std::initializer_list constructor with compatible arguments, that will be chosen over a different constructor if list-initialization is used.

Vector v3 = 3;

That is valid because you have a non-explicit constructor taking an int, so implicit conversions from int to Vector are allowed. That's generally not such a good idea as you can get implicit conversions when you don't want them.

The way to call a non-initializer-list constructor when an initializer-list constructor exists is to use parentheses for the initialization:

Vector v1 (3); //int constructor
Vector v2 {3}; //initializer_list constructor

Generally its best to avoid these logically ambiguous constructors if you can help it, as these rules can be annoying. In this case, since you are emulating std::vector, its probably okay.

like image 108
TartanLlama Avatar answered Dec 23 '25 22:12

TartanLlama



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!