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;
}