问题
Initializing objects with new {} syntax like this:
int a { 123 };
has benefit - you would not declare a function instead of creating a variable by mistake. I even heard that it should be habit to always do that. But see what could happen:
// I want to create vector with 5 ones in it:
std::vector<int> vi{ 5, 1 }; // ups we have vector with 5 and 1.
Is this good habit then? Is there a way to avoid such problems?
回答1:
Frankly, the subtleties of the various initialization techniques make it difficult to say that any one practice is a "good habit."
As mentioned in a comment, Scott Meyers discusses brace-initialization at length in Modern Effective C++. He has made further comments on the matter on his blog, for instance here and here. In that second post, he finally says explicitly that he thinks the morass of C++ initialization vagaries is simply bad language design.
As mentioned in 101010's answer, there are benefits to brace-initialization. The prevention of implicit narrowing is the main benefit, in my opinion. The "most vexing parse" issue is of course a genuine benefit, but it's paltry--it seems to me that in most cases an incorrect int a();
instead of int a;
would probably be caught at compile time.
But there are at least two major drawbacks:
- In C++11 and C++14,
auto
always deducesstd::initializer_list
from a brace-initializer. In C++17, if there's only one element in the initialization list, and=
is not used,auto
deduces the type of that element;the behavior for multiple elements is unchanged(See the second blog post linked above for a clearer explanation, with examples.) (Edit: as pointed out by T.C. in a comment below, my understanding of theC++17
rules forauto
with brace initialization is still not quite right.) All of these behaviors are somewhat surprising and (in mine and Scott Meyers' opinions) annoying and perplexing. - 101010's third listed benefit, which is that initialization list constructors are preferred to all other constructors, is actually a drawback as well as a benefit. You've already mentioned that the behavior of
std::vector<int> vi{ 5, 1 };
is surprising to people familiar withvector
's old two-element constructor. Scott Meyers lists some other examples in Effective Modern C++. Personally, I find this even worse than theauto
deduction behavior (I generally only useauto
with copy initialization, which makes the first issue fairly easy to avoid).
EDIT: It turns out that stupid compiler-implementation decisions can sometimes be another reason to use brace-initialization (though really the #undef
approach is probably more correct).
回答2:
Initializing objects with list initialization should be preferred wherever applicable, because:
Among other benefits list-initialization limits the allowed implicit narrowing conversions.
In particular it prohibits:
- conversion from a floating-point type to an integer type
- conversion from a
long double
todouble
or tofloat
and conversion fromdouble
tofloat
, except where the source is a constant expression and overflow does not occur. - conversion from an integer type to a floating-point type, except where the source is a constant expression whose value can be stored exactly in the target type.
- conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type.
- Another benefit is that is immune to most vexing parse.
- Also, initialization list constructors are preferred over other available constructors, except for the default.
- Also, they're widely available, all STL containers have initialization list constructors.
Now concerning your example, I would say with knowledge comes power. There's a specific constructor for making a vector of 5 ones (i.e., std::vector<int> vi( 5, 1);
).
来源:https://stackoverflow.com/questions/33444814/is-it-good-habit-to-always-initialize-objects-with