Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Isn't using uniform initialization dangerous?

I discovered uniform initialization a few days ago, and I see about everywhere that everyone should use it as much as possible.

However, I can't help thinking that this new syntax is more trouble than it's worth...


First example

Suppose I write a library, in which I have a struct like this:

struct MyStruct
{
    int member0;
    int member1;
}

A user could write something like this using aggregate initialization :

MyStruct myVar = {0, 1}; // member0 = 0 and member1 = 1

Now, let us say that I update my library, and that the struct now looks like this :

struct MyStruct
{
    int member0;
    int member1;

    MyStruct(int p0, int p1) : member0(p1), member1(p0){}
}

Before C++11, the user code would stop compiling, which would force the user to rewrite his code and use the constructor. But now, the code will compile and be interpreted as uniform initialization :

MyStruct myVar = {0, 1}; // member0 = 1 and member1 = 0

Without the user knowing, updating his library will make his code do something very different!


Second example

Now, let us say that I have a class like this in my library :

class MyClass
{
public:
    MyClass(int size, int default = 0) : elements(size, default){}
private:
    std::vector<int> elements;
}

A user can use it like this :

MyClass myVar (3,1);  // 3 elements with value 1

or, using uniform initilization, like this :

MyClass myVar {3,1};  // 3 elements with value 1

Then again, let us say that I update my library. The class now look like this :

class MyClass
{
public:
    MyClass(int size, int default = 0) : elements(size, default){}
    MyClass(std::initializer_list<int> elts) : elements(elts){}
private:
    std::vector<int> elements;
}

There won't be a problem if a classic constructor was used :

MyClass myVar (3,1);  // 3 elements with value 1

but the code interpretation will change if uniform initialization was called :

MyClass myVar {3,1};  // 2 elements with values 3 and 1

Based on these examples, it seems to me to be extremely dangerous for a user to use uniform initialization, since the code interpretation may change when things are added to the used libraries, without any warning at all.

Worse, the introduction of uniform initialization makes aggregate initialization as dangerous.

Have I missed something? Is there a context in which the use of uniform initialization is both safe and useful?

like image 976
Eternal Avatar asked May 04 '14 11:05

Eternal


1 Answers

I think both problems you addressed have very little to do with uniform initialization itself, but illustrate the dangers of changing an interface.

You could archive the very same suboptimal change in users code by updating your library like this:

struct MyStruct
{
    int member1;
    int member0;
}

No uniform initialization involed. It was also possible, pre-c++11, to change the constructor chosen by overload resolution:

class some_class
{
    public:
    some_class(int);
}

User coder:

some_class var(1.0);

If the code were changed to:

class some_class
{
    public:
    some_class(int);
    some_class(double);
}

The second constructor would be called. Again, no uniform initialization involed, yet the same problems occur.

So, whilst both examples do showcase the fact that the meaning of a users code can be changed by changes to a libraries interface, this is not a problem intruduced by or specific to uniform initialization but rather suboptimal design. It merely illustrates the fact that a libraries interface should be designed very carefully.

On the contray, uniform initialization offers some real advantages. For those, see this excellent answer

like image 100
Philipp Lenk Avatar answered Sep 18 '22 12:09

Philipp Lenk