Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Silent breaking of constructor calls after adding initializer_list constructor

Tags:

Let's consider the following:

#include <iostream>
#include <initializer_list>

class Foo {
public:
    Foo(int) {
        std::cout << "with int\n";
    }
};

int main() {
    Foo a{10};  // new style initialization
    Foo b(20);  // old style initialization
}

Upon running it prints:

with int
with int

All good. Now due to new requirements I have added a constructor which takes an initializer list.

Foo(std::initializer_list<int>) {
    std::cout << "with initializer list\n";
}

Now it prints:

with initializer list
with int

So my old code Foo a{10} got silently broken. a was supposed to be initialized with an int.

I understand that the language syntax is considering {10} as a list with one item. But how can I prevent such silent breaking of old code?

  1. Is there any compiler option which will give us warning on such cases? Since this will be compiler specific I'm mostly interested with gcc. I have already tried -Wall -Wextra.
  2. If there is no such option then do we always need to use old style construction, i.e. with () Foo b(20), for other constructors and use {} only when we really meant an initializer list?
like image 859
taskinoor Avatar asked Sep 25 '17 05:09

taskinoor


2 Answers

It's impossible to generate any warning in these cases, because presented behaviour of choosing std::initializer_list constructor over direct matches is well defined and compliant with a standard.

This issue is described in detail in Scott Meyers Effective Modern C++ book Item 7:

If, however, one or more constructors declare a parameter of type std::initializer_list, calls using the braced initialization syntax strongly prefer the overloads taking std::initializer_lists. Strongly. If there’s any way for compilers to construe a call using a braced initializer to be to a constructor taking a std::initializer_list, compilers will employ that interpretation.

He also presents a few of edge cases of this issue, I strongly recommend reading it.

like image 112
Outshined Avatar answered Sep 30 '22 17:09

Outshined


I couldn't find such an option, so apparently in such cases you should use parentheses for classes that have initializer_list constructor, and uniform initialization for all other classes as you wish.

Some useful insights can be found in this answer and comments to it: https://stackoverflow.com/a/18224556/2968646

like image 39
lomereiter Avatar answered Sep 30 '22 18:09

lomereiter