C++ Stories

C++ Stories

C++ Stories

c++ initialize string

C++ is famous… or infamous for its complex initialization syntax. In this article, I’ll show you around 20 ways to initialize simple std::string variables. Can we somehow make it easier to understand?

Default values

Have a look:

We have two local variables (with automatic storage duration), str0 is default initialized, while str1 is value initialized.

While default initialization is unsafe for integers or other built-in types, it’s relatively fine (but less readable) for class types with a default constructor. In that case, a default constructor will be called, and the string object will get an empty value. The same constructor is invoked for value initialization.

Nevertheless, it’s best to set a value:

See the supportive C++ core guideline: C++ Core Guidelines – ES.20: Always initialize an object.

Copy vs. direct

Usually, it’s best to wait until there’s some value. In a case of a simple integer, we have several forms:

While it may look strange that I assign a double value to an integer, the point is that lines with x2 and y2 won’t compile. List initialization prevents narrowing conversions. Have a look at Compiler Explorer.

The same happens for computing value in a helper function (see @Compiler Explorer):

For strings, we have several options:

And its variation with list syntax:

In all cases, the compiler will call the single constructor:

What’s more, the copy syntax doesn’t consider so-called explicit constructors:

For strings, we have, for example an explicit constructor for string_view:

template<class StringViewLike> explicit constexpr basic_string(const StringViewLike& t, const Allocator& alloc = Allocator() );

See an example: (run here):

Braces or not?

Is it better to call braces or regular round parens? Have a look at the following example:

The output:

In the second case, we call:

List initialization has this unwanted consequence that tries to convert a range of values into a single initializer_list (when there’s a constructor taking such an object). If you want to call some special constructor for a container, it’s best to use () as it uses a “regular” function overload call and doesn’t treat initializer_list in a special way.

Non-local scopes

If we move out of the function scope, we can think about at least several options:

The code above doesn’t include module linkage options that we also get with C++20.

As for the initialization, process strings will go through the “dynamic initialization” step for static variables. For trivial types, there can also be constant initialization taking place or zero initialization:

For example:

See my other blog post: What happens to your static variables at the start of the program? – C++ Stories.

Deduction

So far, I explicitly mentioned the type of variables, but we can use auto x = form:

What’s the best form?

C++11 introduced list initialization which tried to become “uniform” initialization. One syntax for all options. Being “uniform” is not that easy, especially taking various historical reasons and C-language compatibility. It’s better with each revision of C++, but there are some exceptions.

C++ Core Guidelines suggests: the following rule “ES.23: Prefer the {}-initializer syntax”

Reason

Prefer {}. The rules for {} initialization are simpler, more general, less ambiguous, and safer than for other forms of initialization. Use = only when you are sure that there can be no narrowing conversions. For built-in arithmetic types, use = only with auto.

Exception

For containers, there is a tradition for using {…} for a list of elements and (…) for sizes:

As you can see, there are lots of options for static variables. In this case, inline variables introduced in C++17 can help greatly. What’s more, it’s best to avoid global state, so think twice if you really have to create such an object.

Additional guides

  • In Item 7 for Effective Modern C++, Scott Meyers said that “braced initialization is the most widely usable initialization syntax, it prevents narrowing conversions, and it’s immune to C++’s most vexing parse.
  • Nicolai Josuttis had an excellent presentation about all corner cases: CppCon 2018: Nicolai Josuttis “The Nightmare of Initialization in C++” – YouTube, and suggests using {}
  • Only abseil / Tip of the Week #88: Initialization: =, (), and {} – prefers the old style. This guideline was updated in 2015, so many things were updated as of C++17 and C++20.
  • In Core C++ 2019 :: Timur Doumler :: Initialisation in modern C++ – YouTube – Timur suggests {} for all, but if you want to be sure about the constructor being called then use (). As () performs regular overload resolution.

Bonus

There’s also at least one other way to initialize data:

Have a look at savedString. It uses a capture clause with an initializer, available since C++14 for lambda expressions. Here’s a note from my book on that feature:

Now, in C++14, you can create new data members and initialize them in the capture clause. Then you can access those variables inside the lambda. It’s called capture with an initializer, or another name for this feature is generalized lambda capture.

So, savedString is technically a data member of an anonymous callable object, but the syntax is quite cool.

Summary

While we can easily come up with a lot of techniques and strange syntax for initialization, I think there’s also a simple way to look at it:

  • Always initialize variables; use {} to value initialize them at least
  • const if posible, or even constexpr
  • use list initialization unless you want to call some specific constructor (like for containers and setting the size)
  • limit the number of global objects

We also haven’t discussed arrays and compounds (in C++20, you can use Designated Initializers (see my post)).

Also, please look at a popular blog post from 2017 Initialization in C++ is bonkers where you can find at least 18 different ways to initialize an integer.

Back to you

  • Can you add some other ways to init a string?
  • What are your tactics for variable initialization?
  • is this an important topic for you? or do you not care much?

Please leave a comment below.

This post was last modified on December 8, 2024 3:27 am