cpp
modern cpp
01_modern_cpp.cpp⚙️cpp
/**
* ============================================================
* C++ MODERN FEATURES (C++11/14/17/20)
* ============================================================
*
* This file covers:
* - Auto and decltype
* - Range-based for loops
* - nullptr and type safety
* - Move semantics overview
* - Structured bindings (C++17)
* - Optional, variant, any (C++17)
* - Concepts preview (C++20)
*
* Compile: g++ -std=c++17 -Wall 01_modern_cpp.cpp -o modern
* Run: ./modern
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <tuple>
#include <optional>
#include <variant>
#include <any>
#include <memory>
#include <algorithm>
#include <initializer_list>
#include <type_traits>
using namespace std;
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ MODERN FEATURES" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// PART 1: AUTO AND TYPE DEDUCTION
// ========================================================
cout << "--- PART 1: AUTO & TYPE DEDUCTION ---" << endl << endl;
// Basic auto
auto integer = 42; // int
auto floating = 3.14; // double
auto text = "hello"s; // std::string (with s suffix)
auto cstr = "hello"; // const char*
cout << "auto integer = 42; // type: int" << endl;
cout << "auto floating = 3.14; // type: double" << endl;
cout << "auto text = \"hello\"s; // type: std::string" << endl;
// Auto with containers
vector<int> nums = {1, 2, 3, 4, 5};
auto it = nums.begin(); // vector<int>::iterator
auto& ref = nums[0]; // int&
const auto& cref = nums[1]; // const int&
cout << "\nauto with containers preserves const/ref qualifiers" << endl;
// decltype - get type without evaluating
int x = 10;
decltype(x) y = 20; // int
decltype((x)) z = x; // int& (parentheses matter!)
cout << "\ndecltype(x) gives: int" << endl;
cout << "decltype((x)) gives: int& (note the parens!)" << endl;
// decltype(auto) - perfect forwarding return type
auto getValue = [](int& v) -> decltype(auto) {
return v; // Returns int&, not int
};
cout << endl;
// ========================================================
// PART 2: RANGE-BASED FOR LOOPS
// ========================================================
cout << "--- PART 2: RANGE-BASED FOR ---" << endl << endl;
vector<string> words = {"Hello", "Modern", "C++"};
// By value (copies)
cout << "By value: ";
for (string word : words) {
cout << word << " ";
}
cout << endl;
// By reference (modifiable)
for (string& word : words) {
word += "!";
}
cout << "Modified: ";
for (const string& word : words) { // By const ref (read-only)
cout << word << " ";
}
cout << endl;
// With auto
cout << "With auto: ";
for (const auto& word : words) {
cout << word << " ";
}
cout << endl;
// With initializer list
cout << "With init list: ";
for (int n : {1, 2, 3, 4, 5}) {
cout << n << " ";
}
cout << endl;
cout << endl;
// ========================================================
// PART 3: NULLPTR AND TYPE SAFETY
// ========================================================
cout << "--- PART 3: NULLPTR ---" << endl << endl;
int* ptr1 = nullptr; // Correct way
// int* ptr2 = NULL; // Old way (avoid)
// int* ptr3 = 0; // Really old way (avoid)
// Type safety
auto checkNull = [](int* p) {
if (p == nullptr) {
cout << "Pointer is null" << endl;
} else {
cout << "Pointer value: " << *p << endl;
}
};
checkNull(ptr1);
int value = 42;
checkNull(&value);
// nullptr_t type
cout << "\nnullptr has type std::nullptr_t" << endl;
cout << "Convertible to any pointer type" << endl;
cout << endl;
// ========================================================
// PART 4: INITIALIZER LISTS
// ========================================================
cout << "--- PART 4: INITIALIZER LISTS ---" << endl << endl;
// Uniform initialization
int arr[] = {1, 2, 3, 4, 5};
vector<int> vec1 = {1, 2, 3, 4, 5};
vector<int> vec2{1, 2, 3, 4, 5}; // Direct initialization
map<string, int> ages = {
{"Alice", 30},
{"Bob", 25}
};
// Custom class with initializer_list
class Numbers {
private:
vector<int> data;
public:
Numbers(initializer_list<int> list) : data(list) {}
void print() const {
for (int n : data) cout << n << " ";
cout << endl;
}
};
Numbers n = {10, 20, 30, 40};
cout << "Custom class with initializer_list: ";
n.print();
// Prevents narrowing conversions
// int x{3.14}; // ERROR: narrowing conversion
int safe{42}; // OK
cout << "Braces prevent narrowing: int x{3.14} won't compile" << endl;
cout << endl;
// ========================================================
// PART 5: STRUCTURED BINDINGS (C++17)
// ========================================================
cout << "--- PART 5: STRUCTURED BINDINGS ---" << endl << endl;
// With pair
pair<string, int> person = {"Alice", 30};
auto [name, age] = person;
cout << "pair: " << name << " is " << age << endl;
// With tuple
tuple<int, double, string> data = {1, 3.14, "hello"};
auto [id, value2, text2] = data;
cout << "tuple: " << id << ", " << value2 << ", " << text2 << endl;
// With map iteration
map<string, int> scores = {{"Alice", 95}, {"Bob", 87}};
cout << "map iteration:" << endl;
for (const auto& [student, score] : scores) {
cout << " " << student << ": " << score << endl;
}
// With arrays
int numbers[3] = {10, 20, 30};
auto [a, b, c] = numbers;
cout << "array: " << a << ", " << b << ", " << c << endl;
// With struct
struct Point { int x, y; };
Point p = {100, 200};
auto [px, py] = p;
cout << "struct: x=" << px << ", y=" << py << endl;
cout << endl;
// ========================================================
// PART 6: OPTIONAL (C++17)
// ========================================================
cout << "--- PART 6: OPTIONAL ---" << endl << endl;
// Function that may or may not return a value
auto findUser = [](int id) -> optional<string> {
map<int, string> users = {{1, "Alice"}, {2, "Bob"}};
auto it = users.find(id);
if (it != users.end()) {
return it->second;
}
return nullopt; // No value
};
// Using optional
if (auto user = findUser(1); user.has_value()) {
cout << "Found user: " << *user << endl;
}
auto user2 = findUser(99);
cout << "User 99: " << user2.value_or("Not found") << endl;
// Create optional
optional<int> opt1; // Empty
optional<int> opt2 = 42; // With value
optional<int> opt3 = nullopt; // Explicitly empty
cout << "\nopt1.has_value() = " << opt1.has_value() << endl;
cout << "opt2.has_value() = " << opt2.has_value() << endl;
cout << "opt2.value() = " << opt2.value() << endl;
cout << endl;
// ========================================================
// PART 7: VARIANT (C++17)
// ========================================================
cout << "--- PART 7: VARIANT ---" << endl << endl;
// Type-safe union
variant<int, double, string> v;
v = 42;
cout << "variant as int: " << get<int>(v) << endl;
v = 3.14;
cout << "variant as double: " << get<double>(v) << endl;
v = "hello";
cout << "variant as string: " << get<string>(v) << endl;
// Check which type
cout << "\nindex() = " << v.index() << " (2 = string)" << endl;
cout << "holds_alternative<string>: " << holds_alternative<string>(v) << endl;
// Visit pattern
variant<int, double, string> values[] = {1, 2.5, "text"};
cout << "\nUsing std::visit:" << endl;
for (const auto& val : values) {
visit([](auto&& arg) {
cout << " Value: " << arg << " (type: " << typeid(arg).name() << ")" << endl;
}, val);
}
cout << endl;
// ========================================================
// PART 8: ANY (C++17)
// ========================================================
cout << "--- PART 8: ANY ---" << endl << endl;
// Can hold any type
any a = 42;
cout << "any as int: " << any_cast<int>(a) << endl;
a = 3.14;
cout << "any as double: " << any_cast<double>(a) << endl;
a = string("hello");
cout << "any as string: " << any_cast<string>(a) << endl;
// Type checking
cout << "\nhas_value(): " << a.has_value() << endl;
cout << "type().name(): " << a.type().name() << endl;
// Safe cast
try {
int val = any_cast<int>(a); // Wrong type!
} catch (const bad_any_cast& e) {
cout << "bad_any_cast caught: " << e.what() << endl;
}
// Reset
a.reset();
cout << "After reset, has_value(): " << a.has_value() << endl;
cout << endl;
// ========================================================
// PART 9: STRING_VIEW (C++17)
// ========================================================
cout << "--- PART 9: STRING_VIEW ---" << endl << endl;
// Non-owning view of string data
string s = "Hello, World!";
string_view sv = s;
cout << "string_view: " << sv << endl;
cout << "Substring: " << sv.substr(7, 5) << endl;
// No allocation for string literals
string_view literal = "This is efficient";
cout << "Literal view: " << literal << endl;
// Useful for function parameters
auto printView = [](string_view str) {
cout << "Received: " << str << endl;
};
printView("C-string"); // No conversion to std::string
printView(s); // Works with std::string
printView(sv); // Works with string_view
cout << endl;
// ========================================================
// PART 10: IF/SWITCH WITH INITIALIZER (C++17)
// ========================================================
cout << "--- PART 10: IF/SWITCH INITIALIZER ---" << endl << endl;
// if with initializer
map<string, int> lookup = {{"one", 1}, {"two", 2}};
if (auto it = lookup.find("two"); it != lookup.end()) {
cout << "Found: " << it->first << " = " << it->second << endl;
}
// 'it' is not visible here - limited scope
// switch with initializer
enum class Status { OK, Error, Pending };
auto getStatus = []() { return Status::OK; };
switch (Status s = getStatus(); s) {
case Status::OK:
cout << "Status: OK" << endl;
break;
case Status::Error:
cout << "Status: Error" << endl;
break;
case Status::Pending:
cout << "Status: Pending" << endl;
break;
}
cout << endl;
// ========================================================
// MODERN C++ SUMMARY
// ========================================================
cout << "--- MODERN C++ SUMMARY ---" << endl << endl;
cout << "C++11:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• auto, decltype" << endl;
cout << "• Range-based for" << endl;
cout << "• Lambda expressions" << endl;
cout << "• Smart pointers (unique_ptr, shared_ptr)" << endl;
cout << "• Move semantics" << endl;
cout << "• nullptr" << endl;
cout << "• Uniform initialization" << endl;
cout << "\nC++14:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Generic lambdas" << endl;
cout << "• decltype(auto)" << endl;
cout << "• make_unique" << endl;
cout << "\nC++17:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Structured bindings" << endl;
cout << "• if constexpr" << endl;
cout << "• optional, variant, any" << endl;
cout << "• string_view" << endl;
cout << "• Filesystem library" << endl;
cout << "• if/switch with initializer" << endl;
cout << "\nC++20:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Concepts" << endl;
cout << "• Ranges" << endl;
cout << "• Coroutines" << endl;
cout << "• Modules" << endl;
cout << "• Three-way comparison (<=>) " << endl;
cout << endl;
cout << "============================================" << endl;
cout << "MODERN C++ FEATURES COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Config parser using optional:
* - Parse key=value pairs
* - Return optional<string> for lookups
* - Support default values
*
* 2. Event system using variant:
* - Different event types (Mouse, Keyboard, etc.)
* - Use visit for event handling
* - Type-safe event dispatch
*
* 3. Property bag using any:
* - Store different types by string key
* - Type-safe retrieval
* - Support for default values
*
* 4. JSON-like value type:
* - variant<null_t, bool, int, double, string, array, object>
* - Recursive structure for arrays/objects
* - Parse/serialize methods
*/