Modern C++ (C++11~C++20) Core Syntax Cheatsheet | auto·Lambda·Smart Pointers·Concepts at a Glance
이 글의 핵심
Modern C++ C++11~C++20 core syntax cheatsheet. auto, range-for, lambda, smart pointers, optional, variant, Concepts, Ranges copy-paste ready summary.
Introduction
Modern C++ (C++11 onwards) introduced auto, lambda, smart pointers, optional, Concepts and many features. This guide is a cheatsheet compressing what’s really used daily in production for quick reference.
What You Will Learn
- Grasp C++11~C++20 core syntax at a glance
- Learn copy-paste ready patterns frequently used in production
- Compare major features by version
- Get code ready for coding tests and production
Table of Contents
- C++11: auto, range-for, lambda, smart pointers
- C++14: Generic lambda, make_unique
- C++17: Structured binding, optional, if constexpr
- C++20: Concepts, Ranges, Coroutine
- Real-World Cases
- Troubleshooting
- Conclusion
C++11: auto, range-for, lambda, smart pointers
1) auto - Type Deduction
auto x = 42; // int
auto d = 3.14; // double
auto s = std::string("hello"); // std::string
auto v = std::vector<int>{1, 2, 3}; // std::vector<int>
// Iterator
auto it = v.begin();
// Function return type
auto add(int a, int b) -> int {
return a + b;
}
2) range-based for
std::vector<int> v = {1, 2, 3, 4, 5};
// Read
for (int x : v) {
std::cout << x << std::endl;
}
// Modify
for (int& x : v) {
x *= 2;
}
// const reference
for (const auto& x : v) {
std::cout << x << std::endl;
}
3) Lambda Expression
// Basic
auto add = [](int a, int b) { return a + b; };
std::cout << add(2, 3) << std::endl; // 5
// Capture
int factor = 10;
auto multiply = [factor](int x) { return x * factor; };
std::cout << multiply(5) << std::endl; // 50
// Capture modes
[=] // Capture by value
[&] // Capture by reference
[x] // Capture x by value only
[&x] // Capture x by reference only
4) Smart Pointers
#include <memory>
// unique_ptr (single ownership)
auto p1 = std::make_unique<int>(42);
std::cout << *p1 << std::endl;
// shared_ptr (shared ownership)
auto p2 = std::make_shared<int>(42);
auto p3 = p2; // Increment reference count
// weak_ptr (prevent circular reference)
std::weak_ptr<int> wp = p2;
if (auto sp = wp.lock()) {
std::cout << *sp << std::endl;
}
5) nullptr
// C++98
int* p = NULL; // Confusing with 0
// C++11
int* p = nullptr; // Type-safe
6) Initializer List
std::vector<int> v = {1, 2, 3, 4, 5};
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
C++14: Generic lambda, make_unique
1) Generic Lambda
auto print = [](auto x) { std::cout << x << std::endl; };
print(42);
print("hello");
print(3.14);
2) make_unique
// C++11
std::unique_ptr<int> p(new int(42));
// C++14
auto p = std::make_unique<int>(42);
3) Return Type Deduction
auto add(int a, int b) {
return a + b; // int deduced
}
C++17: Structured binding, optional, if constexpr
1) Structured Binding
// pair
std::pair<int, std::string> p = {1, "hello"};
auto [id, name] = p;
std::cout << id << ", " << name << std::endl;
// map
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : m) {
std::cout << key << ": " << value << std::endl;
}
// tuple
std::tuple<int, double, std::string> t = {1, 3.14, "hello"};
auto [i, d, s] = t;
2) std::optional
#include <optional>
std::optional<int> find(const std::vector<int>& v, int target) {
for (int x : v) {
if (x == target) return x;
}
return std::nullopt;
}
auto result = find(v, 5);
if (result) {
std::cout << "Found: " << *result << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
// value_or
int value = result.value_or(0);
3) std::variant
#include <variant>
std::variant<int, double, std::string> v;
v = 42;
std::cout << std::get<int>(v) << std::endl;
v = 3.14;
std::cout << std::get<double>(v) << std::endl;
v = "hello";
std::cout << std::get<std::string>(v) << std::endl;
// Visitor pattern
std::visit([](auto&& arg) {
std::cout << arg << std::endl;
}, v);
4) if constexpr
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integer: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Float: " << value << std::endl;
} else {
std::cout << "Other: " << value << std::endl;
}
}
process(42); // Integer
process(3.14); // Float
process("hello"); // Other
5) Class Template Argument Deduction
// C++14
std::pair<int, std::string> p(1, "hello");
// C++17
std::pair p(1, "hello"); // Type deduced
std::vector v{1, 2, 3}; // std::vector<int>
C++20: Concepts, Ranges, Coroutine
1) Concepts
#include <concepts>
template<std::integral T>
T add(T a, T b) {
return a + b;
}
// Custom concept
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
};
template<Addable T>
T sum(T a, T b) {
return a + b;
}
2) Ranges
#include <ranges>
#include <vector>
std::vector<int> v = {1, 2, 3, 4, 5};
// Filter even numbers
auto even = v | std::views::filter([](int x) { return x % 2 == 0; });
// Transform
auto doubled = v | std::views::transform([](int x) { return x * 2; });
// Chain
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 2; });
3) Three-way Comparison Operator
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
};
Point p1{1, 2};
Point p2{1, 3};
if (p1 < p2) {
std::cout << "p1 < p2" << std::endl;
}
Troubleshooting
Problem 1: auto Overuse
Symptom: Code readability decreases
// ❌ Unclear type
auto x = 0; // int? long? size_t?
// ✅ Explicit type
int x = 0;
// Good auto usage
auto it = v.begin(); // Iterator
auto lambda = [](int x) { return x * 2; };
Problem 2: Lambda Capture Dangling
Symptom: Lambda captures invalid reference
// ❌ Dangling reference
auto makeLambda() {
int x = 42;
return [&x]() { return x; }; // x disappears from stack
}
// ✅ Capture by value
auto makeLambda() {
int x = 42;
return [x]() { return x; };
}
Problem 3: shared_ptr Circular Reference
Symptom: Memory leak
#include <memory>
struct Node {
std::shared_ptr<Node> next;
};
// ❌ Circular reference
auto n1 = std::make_shared<Node>();
auto n2 = std::make_shared<Node>();
n1->next = n2;
n2->next = n1; // Circular reference (memory leak)
// ✅ Use weak_ptr
struct NodeFixed {
std::weak_ptr<NodeFixed> next;
};
Problem 4: optional Value Access Without Check
Symptom: std::bad_optional_access exception
std::optional<int> opt;
// ❌ Access without check
// int x = *opt; // Exception
// ✅ Check before access
if (opt) {
int x = *opt;
}
// Or value_or
int x = opt.value_or(0);
Conclusion
Modern C++ greatly improves code quality and productivity.
Key Summary
- C++11
- auto, range-for, lambda, smart pointers
- nullptr, initializer list
- C++14
- Generic lambda, make_unique
- Return type deduction
- C++17
- Structured binding, optional, variant
- if constexpr, class template argument deduction
- C++20
- Concepts, Ranges, Coroutine
- Three-way comparison operator
Adoption Order
| Stage | Feature | Reason |
|---|---|---|
| Stage 1 | auto, range-for, smart pointers | Bug reduction |
| Stage 2 | lambda, optional | Code conciseness |
| Stage 3 | Structured binding, variant | Readability |
| Stage 4 | Concepts, Ranges | Expressiveness |
Core Features by Version
// C++11
auto x = 42;
for (auto& x : v) { /* ....*/ }
auto lambda = [](int x) { return x * 2; };
auto p = std::make_shared<int>(42);
// C++14
auto print = [](auto x) { std::cout << x; };
auto p = std::make_unique<int>(42);
// C++17
auto [key, value] = map.begin();
std::optional<int> opt = find(v, 5);
if constexpr (std::is_integral_v<T>) { /* ....*/ }
// C++20
template<std::integral T>
T add(T a, T b) { return a + b; }
auto even = v | std::views::filter([](int x) { return x % 2 == 0; });
Next Steps
- auto and decltype: C++ auto and decltype
- Range-based for: C++ Range-based for
- C++ Overview: What is C++?
References
- “Effective Modern C++” - Scott Meyers
- “C++17 The Complete Guide” - Nicolai M. Josuttis
- “C++20 The Complete Guide” - Nicolai M. Josuttis
- cppreference: https://en.cppreference.com/ One-line summary: Modern C++ greatly improves code quality and productivity with auto, lambda, smart pointers, optional, and Concepts.