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
There are no compiler warnings and there never will be. It just doesn't make sense to warn on code doing something common like
std::vector vec{1};
Remember that the compiler only warns about really unwanted stuff, like undefined behavior. It has no way of knowing that in the definition above, you meant to call the constructor taking a size argument. For all it knows you actually want to have a vector with one element! It can't read your mind :)
The answer to your second question is basically yes. You can always add a dummy parameter like struct {} dummy;
to avoid using the constructor with initializer list, but really, the only same solution is just to use parentheses instead of braces (or don't break the interface suddenly).
If you want to change every portion of code that uses list initialization, you can delete the initializer list constructor, change them to braces, and then implement the constructor correctly. I would consider such change a breaking one, and deal with it appropriately. The other idea would have been to come up with the initializer list use case beforehand, and implement it right away.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.