Skip to main content

Uniform Initialization

C++11 brace initialization {} - one syntax for all types. Consistent, safe (prevents narrowing), solves gotchas.

One Syntax, Everywhere

Braces {} work for any type: fundamentals, aggregates, classes, arrays, containers. Prevents narrowing conversions.

Motivation: Multiple Syntaxes → One

// Pre-C++11: confusing variety
int a = 5;
int b(5);
int c[] = {1, 2, 3};
Widget w(10);
std::vector<int> v; v.push_back(1);

// C++11: uniform braces
int a{5};
int b{5};
int c[]{1, 2, 3};
Widget w{10};
std::vector<int> v{1, 2, 3}; // ✅ Direct!

Basic Syntax

int x{42};
double d{3.14};
std::string s{"hello"};
int arr[]{1, 2, 3, 4, 5};
std::vector<int> vec{1, 2, 3};
Widget w{10, 20};

Rule: Braces work everywhere, same syntax.

Prevents Narrowing Conversions

Catches silent data loss at compile-time.

// Old style: silent truncation
int x = 3.14; // ⚠️ OK but x = 3 (data loss)
double d = 1000000;
int i = d; // ⚠️ OK but dangerous

// Braces: compile error
int y{3.14}; // ❌ Error: narrowing
int j{d}; // ❌ Error: narrowing

char c = 1000; // ⚠️ OK but truncated
char d{1000}; // ❌ Error: narrowing

Benefit: Catches bugs at compile-time, not runtime.

Safe Zero Initialization

Empty braces = value-initialization (zero for fundamentals).

int x{};        // 0
double d{}; // 0.0
bool b{}; // false
int* ptr{}; // nullptr

std::string s{}; // Default constructor
std::vector<int> v{}; // Default constructor

Safe: No indeterminate values, explicit intent.

Solves Most Vexing Parse

// Pre-C++11
Widget w(); // ❌ Function declaration, not object!

// C++11
Widget w{}; // ✅ Unambiguous: creates object

Fix: Braces cannot declare functions, always create objects.

STL Containers

// Direct initialization with values
std::vector<int> v{1, 2, 3, 4, 5};
std::set<std::string> names{"Alice", "Bob"};
std::map<int, std::string> m{{1, "one"}, {2, "two"}};

// vs Pre-C++11 verbosity
std::vector<int> old;
old.push_back(1);
old.push_back(2);
// ...

Initializer List Priority

Braces prefer std::initializer_list constructor.

std::vector<int> v1(10);     // 10 zeros
std::vector<int> v2{10}; // One element: 10

std::vector<int> v3(10, 5); // 10 elements, each = 5
std::vector<int> v4{10, 5}; // Two elements: 10, 5

Important: Braces create element lists, parentheses call constructors.

Constructor Overload Priority

class Widget {
public:
Widget(int x, int y) {
std::cout << "Regular: " << x << ", " << y << "\n";
}

Widget(std::initializer_list<int> list) {
std::cout << "List: " << list.size() << " elements\n";
}
};

Widget w1(10, 20); // Regular: 10, 20
Widget w2{10, 20}; // List: 2 elements (prefers initializer_list!)

Rule: Braces always prefer initializer_list constructor when available.

When to Use Each

SyntaxUse For
{} bracesDefault choice (safe, consistent)
() parensContainer sizing, avoid initializer_list
// Prefer braces (safety)
int x{42};
std::string s{"hello"};
std::vector<int> v{1, 2, 3}; // Element list

// Use parentheses for sizing
std::vector<int> v(100); // 100 zeros
std::string s(10, 'x'); // "xxxxxxxxxx"

// Avoid initializer_list constructor
Widget w(10, 20); // Call Widget(int, int)

Copy List Initialization

int x = {42};
std::string s = {"hello"};
std::vector<int> v = {1, 2, 3};

// Still prevents narrowing
int y = {3.14}; // ❌ Error

Quick Decision Guide

Key Benefits Summary

FeatureBenefit
Uniform syntaxWorks everywhere
Narrowing preventionCatches type errors
Zero initializationSafe defaults with {}
Most vexing parseSolved
STL containersDirect initialization

Common Gotchas

// 1. Initializer list priority
std::vector<int> v{10}; // ONE element, not size!

// 2. Narrowing errors (feature, not bug)
int x{3.14}; // ❌ Error (this is good!)

// 3. Empty braces call default constructor
Widget w{}; // Calls Widget(), not Widget(initializer_list)

Summary

Uniform Initialization - Key Points

One Syntax for All Types:

  • Fundamentals: int x{42};
  • Arrays: int arr[]{1, 2, 3};
  • Classes: Widget w{10, 20};
  • Containers: std::vector<int> v{1, 2, 3};
  • Empty braces: int x{}; → zero-initialized

Key Safety Features:

  • Prevents narrowing: int x{3.14}; → compile error ✅
  • Catches data loss at compile-time (not runtime)
  • Forces explicit casts for type conversions
  • Safer than traditional = or () syntax

Solves Language Gotchas:

  • Most vexing parse: Widget w{}; unambiguously creates object
  • Traditional: Widget w(); declares function ❌
  • Direct container init: No more verbose push_back loops

Initializer List Priority:

  • Braces prefer std::initializer_list constructor
  • vector<int> v{10} → one element (10), not size
  • vector<int> v(10) → ten zeros
  • Use parentheses to avoid initializer_list constructor

When to Use Each:

  • Braces {}: Default choice (safe, consistent)
  • Parentheses (): Container sizing, avoid initializer_list
  • Assignment =: Legacy code, simple cases

Common Gotchas:

  • Empty braces call default ctor, not initializer_list
  • Narrowing errors are features, not bugs
  • Vector sizing requires parentheses: vector<int>(100)
// Interview answer:
// "Uniform initialization (C++11 braces) provides one syntax
// for all types. Key benefits: prevents narrowing conversions
// at compile-time, solves most vexing parse, enables direct
// container initialization. Empty braces safely zero-initialize.
// Braces prefer initializer_list constructors - use parentheses
// for container sizing. Default to braces for safety unless
// you specifically need parentheses behavior."