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?
-Wall -Wextra
.()
Foo b(20)
, for other constructors and use {}
only when we really meant an initializer list?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 astd::initializer_list
, compilers will employ that interpretation.
He also presents a few of edge cases of this issue, I strongly recommend reading it.