Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 48 additions & 58 deletions _posts/2020-09-01-totw-140.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,21 +429,20 @@ Some modern C++ features are not yet supported by some major compilers.
static constexpr absl::string_view kHello = "Hello";
return kHello;
}
</pre>
</pre>

2. For the `extern const`
[variables declared in header files](#extern-const-variable) the following
2. For the `extern const` [variables declared in header files](#extern-const-variable) the following
approach to defining their values is valid according to the standard C++,
and would in fact be preferrable to ABSL_CONST_INIT, but it is not yet
supported by some compilers.

<pre class="prettyprint lang-cpp bad-code">
// Defined in foo.cc -- valid C++, but not supported by MSVC 19.
constexpr absl::string_view kOtherBufferName = "other example";
</pre>
// Defined in foo.cc -- valid C++, but not supported by MSVC 19.
constexpr absl::string_view kOtherBufferName = "other example";
</pre>

As a workaround for a `constexpr` variable in a `.cc` file you can provide
its value to other files through functions.
As a workaround for a `constexpr` variable in a `.cc` file you can provide
its value to other files through functions.

### Mistake #4: Improperly Initialized Constants

Expand All @@ -455,72 +454,66 @@ depend on the value of `X`? Cyclic initialization dependencies can easily happen
with global variables, especially with those we think of as constants.

This is a pretty thorny area of the language in its own right.
[The style guide](https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables)
["Static and Global Variables" in the Style Guide](https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables)
is an authoritative reference.

Consider the above links required reading. With a focus on initialization of
constants, the phases of initialization can be explained as:

1. **Zero initialization**. This is what initializes otherwise uninitialized
static variables to the "zero" value for the type (e.g. `0`, `0.0`, `'\0'`,
null, etc.).
#### Zero initialization
This is what initializes otherwise uninitialized static variables to the
"zero" value for the type (e.g. `0`, `0.0`, `'\0'`, null, etc.).

<pre class="prettyprint lang-cpp code">
const int kZero; // this will be zero-initialized to 0
const int kLotsOfZeroes[5000]; // so will all of these
</pre>
const int kZero; // this will be zero-initialized to 0
const int kLotsOfZeroes[5000]; // so will all of these
</pre>

Note that relying on zero initialization is fairly popular in C code but is
fairly rare and niche in C++. It is generally clearer to assign variables
explicit values, even if the value is zero, which brings us to...
Note that relying on zero initialization is fairly popular in C code but is
fairly rare and niche in C++. It is generally clearer to assign variables
explicit values, even if the value is zero, which brings us to...

1. **Constant initialization**.
#### Constant initialization

<pre class="prettyprint lang-cpp code">
const int kZero = 0; // this will be constant-initialized to 0
const int kOne = 1; // this will be constant-initialized to 1
</pre>
const int kZero = 0; // this will be constant-initialized to 0
const int kOne = 1; // this will be constant-initialized to 1
</pre>

Both "constant initialization" and "zero initialization" are called "static
initialization" in the C++ language standard. Both are always safe.
Both "constant initialization" and "zero initialization" are called "static
initialization" in the C++ language standard. Both are always safe.

1. **Dynamic initialization**.
#### Dynamic initialization

<pre class="prettyprint lang-cpp bad-code">
// This will be dynamically initialized at run-time to
// whatever ArbitraryFunction returns.
const int kArbitrary = ArbitraryFunction();
</pre>

Dynamic initialization is where most problems happen. The style guide
explains why at
https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables.

Note that documents like the Google C++ style guide have historically
included dynamic initialization in the broad category of "static
initialization." The word "static" applies to a few different concepts in
C++, which can be confusing. "Static initialization" can mean
"initialization *of* static variables," which can include run-time
computation (dynamic initialization). The language standard uses the term
"static initialization" in a different, narrower, sense: initialization that
is done statically or at compile-time.
// This will be dynamically initialized at run-time to
// whatever ArbitraryFunction returns.
const int kArbitrary = ArbitraryFunction();
</pre>

Dynamic initialization is where most problems happen. The style guide explains
why at ["Static and Global Variables"](https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables).

Note that documents like the Google C++ style guide have historically included
dynamic initialization in the broad category of "static initialization." The
word "static" applies to a few different concepts in C++, which can be confusing.
"Static initialization" can mean "initialization *of* static variables," which
can include run-time computation (dynamic initialization). The language standard
uses the term "static initialization" in a different, narrower, sense:
initialization that is done statically or at compile-time.

## Initialization Cheat Sheet

Here is a super-quick constant initialization cheat sheet (not in header files):

1. `constexpr` guarantees safe constant initialization as well as safe
(trivial) destruction. Any `constexpr` variable is entirely fine when
defined in a `.cc` file, but is problematic in header files for reasons
explained earlier.
2. `ABSL_CONST_INIT` guarantees safe constant initialization. Unlike
`constexpr`, it does not actually make the variable `const`, nor does it
ensure the destructor is trivial, so care must still be taken when declaring
static variables with it. See again
https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables.
3. Otherwise, you're most likely best off using a static variable within a
function and returning it. See http://go/cppprimer#static_initialization,
and the "ordinary function" example shown earlier.
1. `constexpr` guarantees safe constant initialization as well as safe (trivial) destruction. Any `constexpr` variable is entirely fine when defined in a `.cc` file, but is problematic in header files for reasons explained earlier.
1. `ABSL_CONST_INIT` guarantees safe constant initialization. Unlike `constexpr`, it does not actually make the variable `const`, nor does it ensure the destructor is trivial, so care must still be taken when declaring static variables with it. See again ["Static and Global Variables" in the Style Guide](https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables).
1. Otherwise, you're most likely best off using a static variable within a function and returning it. See <!-- http://go/cppprimer#static_initialization, and --> the "ordinary function" example shown earlier.

## Conclusion

The `inline` variable from C++17 can't come soon enough. Until then all we can
do is use the safe idioms that steer us clear of the rough edges.

## Further Reading and Collected Links

Expand All @@ -530,7 +523,4 @@ Here is a super-quick constant initialization cheat sheet (not in header files):
* http://en.cppreference.com/w/cpp/language/storage_duration (linkage rules)
* http://en.cppreference.com/w/cpp/language/ub (Undefined Behavior)

## Conclusion

The `inline` variable from C++17 can't come soon enough. Until then all we can
do is use the safe idioms that steer us clear of the rough edges.
## Footnotes