string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
Originally posted as TotW #88 on Jan 27, 2015
by Titus Winters ([email protected]), on behalf of the Google C++ Style Arbiters
C++11 provided a new syntax referred to as “uniform initialization syntax” that was supposed to unify all of the various styles of initialization, avoid the Most Vexing Parse, and avoid narrowing conversions. This new mechanism means we now have yet another syntax for initialization, with its own tradeoffs.
Some uniform initialization syntax proponents would suggest that we use {}s and direct initialization (no use of the ‘=’, although in most cases both forms call the same constructor) for initialization of all types:
int x{2};
std::string foo{"Hello World"};
std::vector<int> v{1, 2, 3};
vs. (for instance):
int x = 2;
std::string foo = "Hello World";
std::vector<int> v = {1, 2, 3};
This approach has two shortcomings. First, “uniform” is a stretch: there are cases where ambiguity still exists (for the casual reader, not the compiler) in what is being called and how.
std::vector<std::string> strings{2}; // A vector of two empty strings.
std::vector<int> ints{2}; // A vector containing only the integer 2.
Second: this syntax is not exactly intuitive: no other common language uses something like it. The language can certainly introduce new and surprising syntax, and there are technical reasons why it’s necessary in some cases – especially in generic code. The important question is: how much should we change our habits and language understanding to take advantage of that change? Are the benefits worth the cost in changing our habits or our existing code? For uniform initialization syntax, we don’t believe in general that the benefits outweigh the drawbacks.
Instead, we recommend the following guidelines for “How do I initialize a variable?”, both to follow in your own code and to cite in your code reviews:
Use assignment syntax when initializing directly with the intended literal
value (for example: int
, float
, or std::string
values), with smart
pointers such as std::shared_ptr
, std::unique_ptr
, with containers
(std::vector
, std::map
, etc), when performing struct initialization, or
doing copy construction.
int x = 2;
std::string foo = "Hello World";
std::vector<int> v = {1, 2, 3};
std::unique_ptr<Matrix> matrix = NewMatrix(rows, cols);
MyStruct x = {true, 5.0};
MyProto copied_proto = original_proto;
instead of:
// Bad code
int x{2};
std::string foo{"Hello World"};
std::vector<int> v{1, 2, 3};
std::unique_ptr<Matrix> matrix{NewMatrix(rows, cols)};
MyStruct x{true, 5.0};
MyProto copied_proto{original_proto};
Use the traditional constructor syntax (with parentheses) when the initialization is performing some active logic, rather than simply composing values together.
Frobber frobber(size, &bazzer_to_duplicate);
std::vector<double> fifty_pies(50, 3.14);
vs.
// Bad code
// Could invoke an intializer list constructor, or a two-argument constructor.
Frobber frobber{size, &bazzer_to_duplicate};
// Makes a vector of two doubles.
std::vector<double> fifty_pies{50, 3.14};
Use {} initialization without the = only if the above options don’t compile:
class Foo {
public:
Foo(int a, int b, int c) : array_{a, b, c} {}
private:
int array_[5];
// Requires {}s because the constructor is marked explicit
// and the type is non-copyable.
EventManager em{EventManager::Options()};
};
Never mix {}s and auto.
For example, don’t do this:
// Bad code
auto x{1};
auto y = {2}; // This is a std::initializer_list<int>!
(For the language lawyers: prefer copy-initialization over direct-initialization when available, and use parentheses over curly braces when resorting to direct-initialization.)
Perhaps the best overall description of the issue is Herb Sutter’s
GotW post. Although he
shows examples that include direct initialization of int
with braces, his
final advice is roughly compatible with what we present here with one caveat:
where Herb says “where you prefer to see only the = sign”, we unambiguously
prefer to see exactly that. In conjunction with more consistent use of
explicit
on multi-parameter constructors (see Tip #142),
this provides a balance between readability, explicitness, and correctness.
The tradeoffs for uniform initialization syntax are not generally worth it: our compilers already warn against the Most Vexing Parse (you can use brace initialization or add parens to resolve the issue), and the safety from narrowing conversions isn’t worth the readability hit for brace-initialization (we’ll need a different solution for narrowing conversions, eventually). The Style Arbiters don’t think this issue is critical enough to make a formal rule on, especially because there are cases (notably in generic code) where brace initialization may be justified.