Modern C++ Core Features and Standard Library Enhancements

Standardization and Historical Context

The C++11 standard, ratified in 2011, marked a significant evolution from C++98/03. It introduced approximate 140 new features alongside numerous defect resolutions, transforming the language to better support modern system and library development with enhanced safety, efficiency, and programmer productivity.

Core Language Enhancements

Uniform Initialization

C++11 establishes a consistent initialization syntax using braces {}, applicable to built-in types, aggregates, classes, and standard libray containers. It prevents narrowing conversions and offers clear syntax.

Code Demonstration

#include <iostream>
#include <vector>

struct Coordinate { int x, y; };
class Widget {
public:
    explicit Widget(int v) : data(v) {}
    int data;
};

int main() {
    // Built-in types and arrays
    int val{100};
    double pi{3.14159};
    int seq[] {1, 2, 3, 4, 5};

    // Aggregates and classes
    Coordinate pos{10, 20};
    Widget w{42};

    // STL containers
    std::vector<int> numbers {5, 4, 3, 2, 1};
    std::list<std::string> items {"alpha", "beta", "gamma"};

    return 0;
}

Type Deduction: auto and decltype

The auto keyword enables automatic type deduction for variables, requiring direct initialization. The decltype specifier queries the type of an expression or entity at compile time, useful in generic programming.

Code Demonstration

#include <iostream>
#include <vector>
#include <type_traits>

int main() {
    // auto examples
    auto counter = 10; // int
    auto ratio = 2.718; // double
    std::vector<float> values{1.1f, 2.2f};
    auto iter = values.begin(); // std::vector<float>::iterator
    int var = 50;
    auto& ref = var; // int&
    auto* ptr = &var; // int*

    // decltype examples
    decltype(var) replica = var; // int
    int a=5, b=10;
    decltype(a+b) total = a+b; // int
    decltype((var)) ref2 = var; // int& (parentheses matter)

    std::cout << std::boolalpha;
    std::cout << "ref2 is reference: " << std::is_reference<decltype(ref2)>::value << '\n';
    return 0;
}

Modern Pointer and Loop Constructs

nullptr provides a type-safe null pointer constant of type std::nullptr_t. Range-based for loops offer concise iteration over sequences.

Code Demonstration

#include <iostream>
#include <array>

int main() {
    // nullptr
    int* p = nullptr;
    if(p == nullptr) { /* safe check */ }

    // Range-based for
    std::array<int, 4> arr {7, 8, 9, 10};
    for(int elem : arr) {
        std::cout << elem << ' ';
    }
    std::cout << '\n';

    // Modifying elements via reference
    for(int& elem : arr) {
        elem *= 2;
    }
    return 0;
}

Smart Pointers for Resource Management

C++11 introduced smart pointers to automate memory management, preventing leaks and dangling pointers.

  • std::unique_ptr: Enforces exclusive ownership; non-copyable.
  • std::shared_ptr: Implements shared ownership with reference counting.
  • std::weak_ptr: Provides a non-owning reference to break cyclic dependencies.

Code Demonstration

#include <memory>
#include <iostream>

class Resource {
public:
    Resource(int id) : id_(id) { std::cout << "Resource " << id_ << " created\n"; }
    ~Resource() { std::cout << "Resource " << id_ << " destroyed\n"; }
    void use() const { std::cout << "Using resource " << id_ << '\n'; }
private:
    int id_;
};

int main() {
    // unique_ptr
    std::unique_ptr<Resource> up(new Resource(1));
    up->use();

    // shared_ptr (prefer make_shared)
    std::shared_ptr<Resource> sp1 = std::make_shared<Resource>(2);
    std::shared_ptr<Resource> sp2 = sp1;

    // weak_ptr example
    std::weak_ptr<Resource> wp = sp1;
    if(auto temp = wp.lock()) {
        temp->use();
    }
    return 0;
}

STL Extensions and New Containers

C++11 enriched the Standard Template Library with new containers, algorithms, and initialization support.

Code Demonstration

#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    // Initializer lists for containers
    std::vector<int> digits {0, 1, 2, 3, 4};

    // Unordered container (hash-based)
    std::unordered_map<std::string, int> wordCount {
        {"the", 100},
        {"a", 50}
    };

    // New algorithms like all_of, any_of
    bool allEven = std::all_of(digits.begin(), digits.end(),
                               [](int n){ return n % 2 == 0; });
    std::cout << "All even? " << std::boolalpha << allEven << '\n';

    return 0;
}

Move Semantics and Rvalue References

Rvalue references (T&&) and move semantics enable efficient transfer of resources from temporary objects, reducing unnecessary copies.

Code Demonstration

#include <iostream>
#include <cstring>
#include <utility>

class Buffer {
    char* data_;
    size_t size_;
public:
    // Constructor
    Buffer(size_t sz) : size_(sz), data_(new char[sz]) {
        std::cout << "Buffer allocated, size=" << size_ << '\n';
    }

    // Move constructor
    Buffer(Buffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
        std::cout << "Buffer moved\n";
    }

    // Move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if(this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
            std::cout << "Buffer move-assigned\n";
        }
        return *this;
    }

    ~Buffer() {
        delete[] data_;
        if(data_) std::cout << "Buffer freed\n";
    }
    // Disable copying
    Buffer(const Buffer&) = delete;
    Buffer& operator=(const Buffer&) = delete;
};

int main() {
    Buffer b1(1024);
    Buffer b2 = std::move(b1); // Move constructor
    Buffer b3(512);
    b3 = std::move(b2);        // Move assignment
    return 0;
}

Variadic Templates and Perfect Forwarding

Variadic templates (typename... Args) accept an arbitrary number of template arguments. Combined with std::forward, they enable perfect forwarding of arguments.

Code Demonstration

#include <iostream>
#include <utility>

// Base case
void log() { std::cout << '\n'; }

// Recursive variadic template
template<typename T, typename... Rest>
void log(T&& first, Rest&&... rest) {
    std::cout << std::forward<T>(first) << ' ';
    log(std::forward<Rest>(rest)...);
}

// Wrapper using perfect forwarding
template<typename... Args>
void printAll(Args&&... args) {
    log(std::forward<Args>(args)...);
}

int main() {
    printAll("Result:", 42, 3.14, 'X');
    return 0;
}

Lambda Expressions

Lambda expressions define anonymous function objects inline, capturing variables from their enclosing scope.

Syntax: [capture](parameters) -> return_type { body }

Code Demonstration

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vals {10, 20, 30, 40, 50};

    // Simple lambda
    std::for_each(vals.begin(), vals.end(),
                  [](int n) { std::cout << n << ' '; });
    std::cout << '\n';

    // Lambda with capture by value
    int threshold = 25;
    auto count = std::count_if(vals.begin(), vals.end(),
                               [threshold](int n) { return n > threshold; });
    std::cout << "Count > " << threshold << ": " << count << '\n';

    // Lambda with capture by reference
    int sum = 0;
    std::for_each(vals.begin(), vals.end(),
                  [&sum](int n) { sum += n; });
    std::cout << "Sum: " << sum << '\n';

    // Lambda with explicit return type
    auto maxFinder = [](int x, int y) -> int { return x > y ? x : y; };
    int maxVal = std::accumulate(vals.begin(), vals.end(), 0, maxFinder);
    std::cout << "Max: " << maxVal << '\n';

    return 0;
}

Function Wrappers and Binders

std::function provides a polymorphic wrapper for any callable target. std::bind creates new callable objects by binding arguments to existing functions.

Code Demonstration

#include <iostream>
#include <functional>

int multiply(int x, int y) { return x * y; }

struct Multiplier {
    int factor;
    int operator()(int n) const { return n * factor; }
};

int main() {
    // std::function
    std::function<int(int,int)> func = multiply;
    std::cout << "Product: " << func(6, 7) << '\n';

    func = [](int a, int b) { return a + b; };
    std::cout << "Sum: " << func(6, 7) << '\n';

    // std::bind
    using namespace std::placeholders;
    auto timesFive = std::bind(multiply, _1, 5);
    std::cout << "10 * 5 = " << timesFive(10) << '\n';

    Multiplier m{3};
    auto boundObj = std::bind(&Multiplier::operator(), &m, _1);
    std::cout << "4 * 3 = " << boundObj(4) << '\n';

    return 0;
}

Concurrency Support

The <thread> libray provides native multithreading, with synchronization primitives like mutexes, condition variables, and atomic operations.

Code Demonstration

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>

std::mutex io_mutex;
std::atomic<int> shared_counter{0};

void worker(int id) {
    for(int i = 0; i < 3; ++i) {
        {
            std::lock_guard<std::mutex> lock(io_mutex);
            std::cout << "Thread " << id << " working\n";
        }
        ++shared_counter;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);

    t1.join();
    t2.join();

    std::cout << "Final counter: " << shared_counter << '\n';
    return 0;
}

Tags: C++11 smart pointers Lambda Expressions move semantics Concurrency

Posted on Tue, 26 May 2026 17:08:55 +0000 by Tonka1979