#010 – The modern way of variable initialization

Three things to take away:

  • Prefer initializing variables when they are created.
  • Brace initialization uses {} and is the modern default style.
  • Brace initialization rejects narrowing conversions, which helps catch mistakes at compile time

Definition, assignment, and initialization

Defining a variable is called … definition.

Giving a variable a value is called assignment.

Initialization merges these two instructions into one statement. E.g. int x { 11 };

This is the modern way of variable initialization.

This is because the creator of C++ and a C++ expert said so. See ES.23 under ES: Expressions and Statements

The historical way is via copy-initialization. E.g. int x = 11;

Copy-initialization has lost support in modern C++ due to being less efficient than other forms of initialization for some complex types.

Brace initialization {}

The syntax is clear. When you see curly braces, you immediately recognize that list initialization is occurring. Additionally, it’s used with other types such as std::string and structs. Thus, adopting brace initialization standardizes the syntax across a codebase.

std::string name { "Ada" };
std::vector<int> values { 1, 2, 3, 4 };

Other benefits:

  1. Allows us to more cleanly initialize multiple variables in a single line
int x = 5, y = 11, z = 12; // Standard initialization
int x{5}, y{11}, z{12};    // List initialization (Uniform Initialization)

2. Used to cleanly initialize a struct

    // Struct definition
    struct Player {
        int id;
        double health;
        int score;
    };
    
    // Let's list initialize our struct
    Player hero{1, 95.5, 1200};
    
    // Traditional way is:
    Player p;
    p.id = 1; p.health = 95.5; p.score = 1200;

    The major advantage: no narrowing conversions

    A special advantage of list-initialization is it forbids narrowing conversions. This is

    A narrowing conversion is a conversion that may lose information.

    For example:

    int x { 4.5 };

    This does not compile, because there is a mismatch between types.

    The compiler catches the mistake immediately.

    With copy initialization, this kind of conversion may be accepted by some compilers, often with a warning:

    int x = 4.5; // x becomes 4 on many compilers

    That is risky. The value 4.5 silently becomes 4.

    Brace initialization prevents this, therefore making our programs more robust.