CodeNebula
C++03corebeginner

Value Initialization (Refined Rules)

C++03 · The Stability Patch · Compiler flag: -std=c++03

Explanation

C++03 refined the rules for value initialization, establishing a clear three-step process: if a type has a user-declared constructor, that constructor is called; if it is a non-union class without a user-declared constructor, every member is value-initialized; otherwise, the object is zero-initialized. This eliminated inconsistencies in C++98 where the behavior of `T()` was ambiguous for certain class types, giving developers a reliable way to guarantee clean initial state.

Real-World Scenario

In an embedded sensor monitoring system, a telemetry struct accumulates readings over time. When resetting between sampling windows, value initialization guarantees every numeric field starts at zero and every nested object is properly default-constructed, preventing stale data from leaking between measurement cycles.

Quick Snippet

snippet.cpp
#include <iostream>

struct SensorReading {
    double temperature;
    double humidity;
    int    sampleCount;
};

int main() {
    // Value initialization: all members guaranteed zero-initialized
    SensorReading baseline = SensorReading();

    std::cout << "Temperature: " << baseline.temperature  << "\n";  // 0.0
    std::cout << "Humidity:    " << baseline.humidity      << "\n";  // 0.0
    std::cout << "Samples:     " << baseline.sampleCount   << "\n";  // 0

    // Also works with new-expressions
    SensorReading* heapReading = new SensorReading();
    std::cout << "Heap temp:   " << heapReading->temperature << "\n"; // 0.0
    delete heapReading;

    return 0;
}

Full Example

value_initialization.cpp
// CodeNebula - C++03 Example: Value Initialization (Refined Rules)
// Compile: g++ -std=c++03 -Wall -Wextra -o value_initialization value_initialization.cpp
//
// C++03 refined the rules for value initialization using T().
// Key guarantee: T() zero-initializes POD types and their members,
// even without a user-defined constructor.

#include <iostream>
#include <cstring>

// ============================================================
// 1. POD struct — no constructor
// ============================================================
struct SensorReading {
    int   id;
    float temperature;
    bool  active;
};

// ============================================================
// 2. Non-POD struct — has a constructor
// ============================================================
struct Config {
    int    timeout;
    double rate;
    bool   enabled;

    Config() : timeout(0), rate(0.0), enabled(false) {}
};

// ============================================================
// 3. Nested POD — struct containing another struct
// ============================================================
struct Point {
    int x;
    int y;
};

struct Rectangle {
    Point topLeft;
    Point bottomRight;
    int   color;
};

// Helper: print raw bytes of a memory region
void printBytes(const void* ptr, std::size_t size) {
    const unsigned char* bytes = static_cast<const unsigned char*>(ptr);
    bool allZero = true;
    for (std::size_t i = 0; i < size; ++i) {
        if (bytes[i] != 0) {
            allZero = false;
            break;
        }
    }
    std::cout << (allZero ? "all zeros" : "NOT all zeros") << std::endl;
}

int main() {
    std::cout << "=== C++03 Value Initialization ===" << std::endl;

    // --------------------------------------------------------
    // Demo 1: Value-initialized POD (guaranteed zero in C++03)
    // --------------------------------------------------------
    std::cout << "\n--- Demo 1: Value-Initialized POD ---" << std::endl;
    SensorReading sensor = SensorReading();  // value-initialization

    std::cout << "sensor.id:          " << sensor.id << std::endl;
    std::cout << "sensor.temperature: " << sensor.temperature << std::endl;
    std::cout << "sensor.active:      " << sensor.active << std::endl;
    std::cout << "Raw bytes: ";
    printBytes(&sensor, sizeof(sensor));

    // --------------------------------------------------------
    // Demo 2: Value-initialized built-in types
    // --------------------------------------------------------
    std::cout << "\n--- Demo 2: Value-Initialized Scalars ---" << std::endl;
    int    i = int();
    double d = double();
    bool   b = bool();
    char   c = char();

    std::cout << "int():    " << i << std::endl;
    std::cout << "double(): " << d << std::endl;
    std::cout << "bool():   " << b << std::endl;
    std::cout << "char():   " << static_cast<int>(c) << std::endl;

    // --------------------------------------------------------
    // Demo 3: Value-initialized array via new
    // --------------------------------------------------------
    std::cout << "\n--- Demo 3: Value-Initialized Dynamic Array ---" << std::endl;
    int* arr = new int[5]();  // all elements zero-initialized
    std::cout << "Dynamic array: ";
    for (int idx = 0; idx < 5; ++idx) {
        std::cout << arr[idx] << " ";
    }
    std::cout << std::endl;
    delete[] arr;

    // --------------------------------------------------------
    // Demo 4: Nested POD value-initialization
    // --------------------------------------------------------
    std::cout << "\n--- Demo 4: Nested POD ---" << std::endl;
    Rectangle rect = Rectangle();

    std::cout << "rect.topLeft.x:     " << rect.topLeft.x << std::endl;
    std::cout << "rect.topLeft.y:     " << rect.topLeft.y << std::endl;
    std::cout << "rect.bottomRight.x: " << rect.bottomRight.x << std::endl;
    std::cout << "rect.bottomRight.y: " << rect.bottomRight.y << std::endl;
    std::cout << "rect.color:         " << rect.color << std::endl;
    std::cout << "Raw bytes: ";
    printBytes(&rect, sizeof(rect));

    // --------------------------------------------------------
    // Demo 5: Non-POD with constructor (calls constructor)
    // --------------------------------------------------------
    std::cout << "\n--- Demo 5: Non-POD (constructor called) ---" << std::endl;
    Config cfg = Config();  // calls user-defined constructor

    std::cout << "cfg.timeout: " << cfg.timeout << std::endl;
    std::cout << "cfg.rate:    " << cfg.rate << std::endl;
    std::cout << "cfg.enabled: " << cfg.enabled << std::endl;

    // --------------------------------------------------------
    // Demo 6: Heap-allocated value-initialized object
    // --------------------------------------------------------
    std::cout << "\n--- Demo 6: Heap Value-Init ---" << std::endl;
    SensorReading* heapSensor = new SensorReading();  // zero-initialized
    std::cout << "heapSensor->id:          " << heapSensor->id << std::endl;
    std::cout << "heapSensor->temperature: " << heapSensor->temperature << std::endl;
    std::cout << "heapSensor->active:      " << heapSensor->active << std::endl;
    delete heapSensor;

    std::cout << "\n=== All value-initialized members are guaranteed zero ===" << std::endl;

    return 0;
}

// Expected Output:
// === C++03 Value Initialization ===
//
// --- Demo 1: Value-Initialized POD ---
// sensor.id:          0
// sensor.temperature: 0
// sensor.active:      0
// Raw bytes: all zeros
//
// --- Demo 2: Value-Initialized Scalars ---
// int():    0
// double(): 0
// bool():   0
// char():   0
//
// --- Demo 3: Value-Initialized Dynamic Array ---
// Dynamic array: 0 0 0 0 0
//
// --- Demo 4: Nested POD ---
// rect.topLeft.x:     0
// rect.topLeft.y:     0
// rect.bottomRight.x: 0
// rect.bottomRight.y: 0
// rect.color:         0
// Raw bytes: all zeros
//
// --- Demo 5: Non-POD (constructor called) ---
// cfg.timeout: 0
// cfg.rate:    0
// cfg.enabled: 0
//
// --- Demo 6: Heap Value-Init ---
// heapSensor->id:          0
// heapSensor->temperature: 0
// heapSensor->active:      0
//
// === All value-initialized members are guaranteed zero ===
Run command
g++ -std=c++03 -Wall -Wextra -o value_initialization value_initialization.cpp