When writing C++, do you often find yourself struggling with whether to use <span>=</span> or <span>()</span> to initialize variables?
Sometimes you write it as <span>int a = 10;</span>
Other times you write it as <span>std::string s("hi");</span>
Can class member variables only be initialized through constructors? 🤯
To address the above issues, C++11 introduced a powerful feature: Uniform Initialization.
In simple terms, you can use a pair of braces <span>{}</span> to uniformly initialize objects.
Today, we will introduce this feature.
Braces Can Initialize Everything ✨
Whether it’s built-in types, arrays, or standard containers, you can use <span>{}</span> to initialize them:
int a{10}; // Built-in type
std::string s{"hello"}; // Class type
int arr[3]{1, 2, 3}; // Array
std::vector v{1, 2, 3, 4}; // Container
What used to be a variety of initialization methods can now be uniformly done with braces, making it easier ✅.
Safer: Avoid Narrowing Conversions 🚫
You may have written code like this:
int x = 3.5; // Compiles, but x = 3, losing the decimal part
This is known as narrowing conversion, where data is forcibly crammed into a smaller type, resulting in a silent loss of precision.
If you switch to using <span>{}</span>:
int x{3.5}; // ❌ Compilation error, directly prevented
Thus, uniform initialization helps us maintain type safety.
More Elegant Member Initialization 🏗️
Previously, we often had to assign values to members in the constructor; now we can directly use braces at the time of definition, making the code look cleaner and safer:
class Point {
public:
int x{0};
int y{0};
public:
Point() = default;
Point(int x, int y) : x{x}, y{y} {}
};
Point p1; // x = 0, y = 0
Point p2{3, 4}; // x = 3, y = 4
The benefits of this approach are:
- Each member has a clear default value at declaration
- The constructor only needs to focus on differentiated assignments
- Reduces bugs caused by forgotten initializations
The “Pitfall” of initializer_list ⚠️
Here’s a common “pitfall” to be particularly aware of:If a class defines a <span>std::initializer_list</span> constructor, then <span>{}</span> will prioritize matching it.
class Foo {
public:
Foo(int x, double y) {
std::cout << "int, double" << std::endl;
}
Foo(std::initializer_list) {
std::cout << "initializer_list" << std::endl;
}
};
Foo f1{1, 2}; // Outputs "initializer_list"
Foo f2(1, 2); // Outputs "int, double"
👉 Therefore, if you only want to use the regular constructor but the class has an <span>initializer_list</span> constructor, then use <span>()</span>.
Conclusion 💡
Uniform initialization is a small change introduced in C++11, but it greatly enhances the consistency, safety, and readability of the code.
If you have been hesitating between using <span>=</span> and <span>()</span>, then from now on, consider using <span>{}</span> more often — braces are the way to go 🚀.