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()
auto
for Variable DeclarationsOriginally posted as TotW #172 on December 11, 2019
By Aaron Jacobs
Updated 2020-04-06
Quicklink: abseil.io/tips/172
Designated initializers are a syntax in the C++20 standard for specifying the contents of a struct in a compact yet readable and maintainable manner. Instead of the repetitive
struct Point { double x; double y; double z; }; Point point; point.x = 3.0; point.y = 4.0; point.z = 5.0;
one can use designated initializers to write
Point point = { .x = 3.0, .y = 4.0, .z = 5.0, };
This is a little less repetitive, but more importantly, can be used in more
contexts. For example, it means the struct can be made const
without resorting
to awkward workarounds:
// Make it clear to the reader (of the potentially complicated larger piece of // code) that this struct will never change. const Point character_position = { .x = 3.0 };
Or can be used directly in a function call without introducing an additional identifier into the scope:
std::vector<Point> points; [...] points.push_back(Point{.x = 3.0, .y = 3.0}); points.push_back(Point{.x = 4.0, .y = 4.0});
Designated initializers are a form of
aggregate initialization, and so can be used only
with aggregates. This means approximately “structs or classes with no
user-provided constructors or virtual functions”, which in turn is approximately
when we use struct
(as opposed to class
) in typical Google style.
The semantics of C++20 designated initializers are what you might expect given other C++ language features like member initialization lists in constructors. Explicitly mentioned fields are initialized, in order, with the expression provided, and it is permissible to leave out fields that you want to have “default” behavior for:
Point point = { .x = 1.0, // y will be 0.0 .z = 2.0, };
What does “default” mean above? Outside of special cases like union
s the
answer is:
std::string foo = "default value";
) then that
is used.= {}
. In practice this means
that for plain old data types you get the zero value, and for more
complicated classes you get a default-constructed instance.This is typically the least surprising behavior. See the standard for details.
Designated initializers have been a standard part of the C language since C99, and have been offered by compilers as a non-standard extension since before that. But until recently they were not part of C++: a notable example where C is not a subset of C++. For this reason the Google style guide used to say not to use them.
After two decades the situation has finally changed: designated initializers are now part of the C++20 standard.
The C++20 form of designated initializers has some restrictions compared to the C version:
Point{.y = 1.0, .x = 2.0}
is
not legal). C does not require this.Point{1.0,
.z = 2.0}
), but C++20 does not.