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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With