C++20 ์ฌํ
C++20 ์ฌํ¶
๊ฐ์¶
C++20์ C++11 ์ดํ ๊ฐ์ฅ ํฐ ๋ณํ๋ฅผ ๊ฐ์ ธ์จ ํ์ค์ ๋๋ค. Concepts, Ranges, Coroutines, Modules ๋ฑ ํ์ ์ ์ธ ๊ธฐ๋ฅ๋ค์ด ์ถ๊ฐ๋์์ต๋๋ค. ์ด ์ฅ์์๋ C++20์ ํต์ฌ ๊ธฐ๋ฅ๋ค์ ํ์ตํฉ๋๋ค.
๋์ด๋: โญโญโญโญโญ
์ ์ ์ง์: ํ ํ๋ฆฟ, ๋๋ค, ์ค๋งํธ ํฌ์ธํฐ
๋ชฉ์ฐจ¶
Concepts¶
Concepts๋?¶
ํ ํ๋ฆฟ ๋งค๊ฐ๋ณ์์ ๋ํ ์ ์ฝ ์กฐ๊ฑด์ ์ ์ํ๋ ๊ธฐ๋ฅ์ ๋๋ค. ์ด์ ์ SFINAE๋ณด๋ค ํจ์ฌ ๊ฐ๋ ์ฑ์ด ์ข์ต๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
#include <concepts>
#include <iostream>
// concept ์ ์
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// concept ์ฌ์ฉ
template<Numeric T>
T add(T a, T b) {
return a + b;
}
// ๋๋ requires ์ ์ฌ์ฉ
template<typename T>
requires Numeric<T>
T multiply(T a, T b) {
return a * b;
}
// ๋๋ ํํ requires
template<typename T>
T divide(T a, T b) requires Numeric<T> {
return a / b;
}
int main() {
std::cout << add(1, 2) << "\n"; // OK
std::cout << add(1.5, 2.5) << "\n"; // OK
// add("hello", "world"); // ์ปดํ์ผ ์๋ฌ!
return 0;
}
ํ์ค Concepts¶
#include <concepts>
// ํ์
๊ด๋ จ
std::same_as<T, U> // T์ U๊ฐ ๊ฐ์ ํ์
std::derived_from<D, B> // D๊ฐ B์ ํ์ ํด๋์ค
std::convertible_to<From, To>// From์ด To๋ก ๋ณํ ๊ฐ๋ฅ
// ์ฐ์ ๊ด๋ จ
std::integral<T> // ์ ์ ํ์
std::floating_point<T> // ๋ถ๋์์์ ํ์
std::signed_integral<T> // ๋ถํธ ์๋ ์ ์
std::unsigned_integral<T> // ๋ถํธ ์๋ ์ ์
// ๋น๊ต ๊ด๋ จ
std::equality_comparable<T> // == ์ฐ์ฐ ๊ฐ๋ฅ
std::totally_ordered<T> // <, >, <=, >= ์ฐ์ฐ ๊ฐ๋ฅ
// ํธ์ถ ๊ด๋ จ
std::invocable<F, Args...> // F(Args...)๊ฐ ํธ์ถ ๊ฐ๋ฅ
std::predicate<F, Args...> // F(Args...)๊ฐ bool ๋ฐํ
์ปค์คํ Concept ์ ์¶
#include <concepts>
#include <string>
// ๋ฌธ์์ด์ฒ๋ผ ๋์ํ๋ ํ์
template<typename T>
concept StringLike = requires(T t) {
{ t.length() } -> std::convertible_to<std::size_t>;
{ t.c_str() } -> std::same_as<const char*>;
{ t[0] } -> std::convertible_to<char>;
};
// ์ปจํ
์ด๋ concept
template<typename T>
concept Container = requires(T t) {
typename T::value_type;
typename T::iterator;
{ t.begin() } -> std::same_as<typename T::iterator>;
{ t.end() } -> std::same_as<typename T::iterator>;
{ t.size() } -> std::convertible_to<std::size_t>;
};
// ์ฌ์ฉ
template<Container C>
void printContainer(const C& container) {
for (const auto& item : container) {
std::cout << item << " ";
}
std::cout << "\n";
}
requires ํํ์¶
// ๋จ์ ์๊ตฌ์ฌํญ
template<typename T>
concept Addable = requires(T a, T b) {
a + b; // ์ด ํํ์์ด ์ ํจํด์ผ ํจ
};
// ํ์
์๊ตฌ์ฌํญ
template<typename T>
concept HasValueType = requires {
typename T::value_type;
};
// ๋ณตํฉ ์๊ตฌ์ฌํญ
template<typename T>
concept Hashable = requires(T t) {
{ std::hash<T>{}(t) } -> std::convertible_to<std::size_t>;
};
// ์ค์ฒฉ ์๊ตฌ์ฌํญ
template<typename T>
concept Sortable = requires(T t) {
requires std::totally_ordered<typename T::value_type>;
{ t.begin() } -> std::random_access_iterator;
};
Concept์ ์ด์ฉํ ์ค๋ฒ๋ก๋ฉ¶
#include <concepts>
#include <iostream>
template<std::integral T>
void print(T value) {
std::cout << "Integer: " << value << "\n";
}
template<std::floating_point T>
void print(T value) {
std::cout << "Float: " << value << "\n";
}
template<typename T>
void print(T value) {
std::cout << "Other: " << value << "\n";
}
int main() {
print(42); // Integer: 42
print(3.14); // Float: 3.14
print("hello"); // Other: hello
return 0;
}
Ranges¶
Ranges๋?¶
์ปจํ ์ด๋์ ์๊ณ ๋ฆฌ์ฆ์ ๋ ์ฐ์ํ๊ฒ ๋ค๋ฃจ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ์ดํ๋ผ์ธ ์คํ์ผ์ ์ฐ์ฐ์ ์ง์ํฉ๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ๊ธฐ์กด ๋ฐฉ์
// for (auto it = nums.begin(); it != nums.end(); ++it) { ... }
// Ranges ๋ฐฉ์
for (int n : nums | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })) {
std::cout << n << " "; // 4 16 36 64 100
}
return 0;
}
Views (๋ทฐ)¶
๋ทฐ๋ ์ง์ฐ ํ๊ฐ๋๋ฉฐ, ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ์ง ์์ต๋๋ค.
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// filter: ์กฐ๊ฑด์ ๋ง๋ ์์๋ง
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });
// transform: ๋ณํ
auto squared = v | std::views::transform([](int x) { return x * x; });
// take: ์ฒ์ n๊ฐ
auto first3 = v | std::views::take(3);
// drop: ์ฒ์ n๊ฐ ์ ์ธ
auto afterFirst3 = v | std::views::drop(3);
// reverse: ์ญ์
auto reversed = v | std::views::reverse;
// ์กฐํฉ
auto result = v | std::views::filter([](int x) { return x > 3; })
| std::views::transform([](int x) { return x * 2; })
| std::views::take(3);
for (int n : result) {
std::cout << n << " "; // 8 10 12
}
return 0;
}
์ฃผ์ Views¶
#include <ranges>
namespace views = std::views;
// ์์ฑ ๋ทฐ
auto r1 = views::iota(1, 10); // 1, 2, ..., 9
auto r2 = views::iota(1) | views::take(10); // ๋ฌดํ ์ํ์ค์์ 10๊ฐ
// ๋ณํ ๋ทฐ
auto r3 = v | views::transform(func);
auto r4 = v | views::filter(pred);
auto r5 = v | views::take(n);
auto r6 = v | views::drop(n);
auto r7 = v | views::take_while(pred);
auto r8 = v | views::drop_while(pred);
auto r9 = v | views::reverse;
// ๋ถํ ๋ทฐ
auto r10 = str | views::split(' '); // ๊ณต๋ฐฑ์ผ๋ก ๋ถํ
// ์ ํฉ ๋ทฐ
auto r11 = nested | views::join; // ์ค์ฒฉ range ํํํ
// ์์ ๋ทฐ
auto r12 = pairs | views::elements<0>; // ํํ์ ์ฒซ ๋ฒ์งธ ์์
auto r13 = pairs | views::keys; // map์ ํค
auto r14 = pairs | views::values; // map์ ๊ฐ
Range ์๊ณ ๋ฆฌ์ฆ¶
#include <ranges>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
// ๋ฒ์ ๊ธฐ๋ฐ ์๊ณ ๋ฆฌ์ฆ
std::ranges::sort(v);
std::ranges::reverse(v);
auto it = std::ranges::find(v, 5);
bool found = std::ranges::contains(v, 5);
int count = std::ranges::count_if(v, [](int x) { return x > 3; });
auto [min, max] = std::ranges::minmax(v);
// ํ๋ก์ ์
struct Person {
std::string name;
int age;
};
std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}};
std::ranges::sort(people, {}, &Person::age); // age๋ก ์ ๋ ฌ
return 0;
}
Coroutines¶
Coroutines๋?¶
์คํ์ ์ผ์ ์ค๋จํ๊ณ ๋์ค์ ์ฌ๊ฐํ ์ ์๋ ํจ์์ ๋๋ค.
๊ธฐ๋ณธ ๊ตฌ์กฐ¶
#include <coroutine>
#include <iostream>
// ์ฝ๋ฃจํด ๋ฐํ ํ์
template<typename T>
struct Generator {
struct promise_type {
T current_value;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
void return_void() {}
};
std::coroutine_handle<promise_type> handle;
explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }
Generator(Generator&& other) noexcept : handle(other.handle) {
other.handle = nullptr;
}
bool next() {
if (!handle.done()) {
handle.resume();
}
return !handle.done();
}
T value() const {
return handle.promise().current_value;
}
};
// ์ฝ๋ฃจํด ํจ์
Generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // ๊ฐ์ yieldํ๊ณ ์ผ์ ์ค๋จ
}
}
int main() {
auto gen = range(1, 5);
while (gen.next()) {
std::cout << gen.value() << " "; // 1 2 3 4
}
return 0;
}
co_await, co_yield, co_return¶
// co_yield: ๊ฐ์ ์ฐ์ถํ๊ณ ์ผ์ ์ค๋จ
Generator<int> numbers() {
co_yield 1;
co_yield 2;
co_yield 3;
}
// co_return: ์ฝ๋ฃจํด ์ข
๋ฃ
Task<int> compute() {
// ๋น๋๊ธฐ ์์
...
co_return 42;
}
// co_await: awaitable ๊ฐ์ฒด ๋๊ธฐ
Task<void> asyncWork() {
auto result = co_await asyncOperation();
// result ์ฌ์ฉ...
}
์ค์ฉ์ ์ธ ์: ๊ฐ๋จํ Task¶
#include <coroutine>
#include <optional>
#include <iostream>
template<typename T>
struct Task {
struct promise_type {
std::optional<T> result;
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_value(T value) {
result = value;
}
};
std::coroutine_handle<promise_type> handle;
Task(std::coroutine_handle<promise_type> h) : handle(h) {}
~Task() { if (handle) handle.destroy(); }
T get() {
return *handle.promise().result;
}
};
Task<int> asyncAdd(int a, int b) {
co_return a + b;
}
int main() {
auto task = asyncAdd(10, 20);
std::cout << "Result: " << task.get() << "\n";
return 0;
}
Modules¶
Modules๋?¶
ํค๋ ํ์ผ์ ๋จ์ ์ ํด๊ฒฐํ๋ ์๋ก์ด ์ฝ๋ ๊ตฌ์ฑ ๋ฐฉ์์ ๋๋ค.
๋ชจ๋ ์ ์¶
// math.cppm (๋ชจ๋ ์ธํฐํ์ด์ค)
export module math;
export int add(int a, int b) {
return a + b;
}
export int multiply(int a, int b) {
return a * b;
}
// ๋ด๋ถ ๊ตฌํ (export ์ ํจ)
int helper() {
return 42;
}
๋ชจ๋ ์ฌ์ฉ¶
// main.cpp
import math;
import <iostream>;
int main() {
std::cout << add(1, 2) << "\n";
std::cout << multiply(3, 4) << "\n";
return 0;
}
์ปดํ์ผ (GCC ์)¶
# ๋ชจ๋ ์ปดํ์ผ
g++ -std=c++20 -fmodules-ts -c math.cppm
# ๋ฉ์ธ ์ปดํ์ผ ๋ฐ ๋งํฌ
g++ -std=c++20 -fmodules-ts main.cpp math.o -o main
๋ชจ๋ ์ฅ์ ¶
| ๊ธฐ์กด ํค๋ | ๋ชจ๋ |
|---|---|
| ๋งค๋ฒ ํ์ฑ | ํ ๋ฒ๋ง ์ปดํ์ผ |
| ๋งคํฌ๋ก ์ค์ผ | ๊ฒฉ๋ฆฌ๋จ |
| ํฌํจ ์์ ์ค์ | ์์ ๋ฌด๊ด |
| ๋๋ฆฐ ๋น๋ | ๋น ๋ฅธ ๋น๋ |
๊ธฐํ C++20 ๊ธฐ๋ฅ¶
์ผํญ ๋น๊ต ์ฐ์ฐ์ (Spaceship Operator)¶
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
// ==, !=, <, >, <=, >= ์๋ ์์ฑ
};
int main() {
Point p1{1, 2}, p2{1, 3};
if (p1 < p2) { /* ... */ }
if (p1 == p2) { /* ... */ }
auto result = p1 <=> p2;
if (result < 0) { /* p1 < p2 */ }
return 0;
}
์ง์ ์ด๊ธฐํ์¶
struct Config {
int width = 800;
int height = 600;
bool fullscreen = false;
const char* title = "App";
};
int main() {
// C++20 ์ง์ ์ด๊ธฐํ
Config cfg{
.width = 1920,
.height = 1080,
.fullscreen = true
// title์ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
};
return 0;
}
consteval๊ณผ constinit¶
// consteval: ๋ฐ๋์ ์ปดํ์ผ ํ์์ ํ๊ฐ
consteval int square(int n) {
return n * n;
}
constexpr int a = square(5); // OK
// int b = square(x); // ์๋ฌ! x๊ฐ ์์๊ฐ ์๋
// constinit: ์ ์ ์ด๊ธฐํ ๊ฐ์
constinit int global = 42;
// constinit int bad = foo(); // ์๋ฌ! foo()๊ฐ constexpr ์๋
std::span¶
#include <span>
#include <vector>
#include <array>
void process(std::span<int> data) {
for (int& n : data) {
n *= 2;
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
std::vector<int> vec = {1, 2, 3, 4, 5};
std::array<int, 5> stdArr = {1, 2, 3, 4, 5};
process(arr); // OK
process(vec); // OK
process(stdArr); // OK
return 0;
}
std::format¶
#include <format>
#include <iostream>
int main() {
std::string s = std::format("Hello, {}!", "World");
std::cout << s << "\n";
std::cout << std::format("{:>10}", 42) << "\n"; // ์ค๋ฅธ์ชฝ ์ ๋ ฌ
std::cout << std::format("{:08x}", 255) << "\n"; // 16์ง์, 0 ์ฑ์
std::cout << std::format("{:.2f}", 3.14159) << "\n"; // ์์์ 2์๋ฆฌ
return 0;
}
std::source_location¶
#include <source_location>
#include <iostream>
void log(const std::string& msg,
const std::source_location& loc = std::source_location::current()) {
std::cout << loc.file_name() << ":"
<< loc.line() << " "
<< loc.function_name() << ": "
<< msg << "\n";
}
int main() {
log("Hello!"); // main.cpp:15 main: Hello!
return 0;
}
C++23 ๋ฏธ๋ฆฌ๋ณด๊ธฐ¶
std::expected¶
#include <expected>
#include <string>
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}
int main() {
auto result = divide(10, 2);
if (result) {
std::cout << "Result: " << *result << "\n";
} else {
std::cout << "Error: " << result.error() << "\n";
}
return 0;
}
std::print¶
#include <print>
int main() {
std::print("Hello, {}!\n", "World");
std::println("Value: {}", 42); // ์๋ ์ค๋ฐ๊ฟ
return 0;
}
std::generator (C++23)¶
#include <generator>
std::generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i;
}
}
int main() {
for (int n : range(1, 10)) {
std::cout << n << " ";
}
return 0;
}
์ฐ์ต ๋ฌธ์ ¶
๋ฌธ์ 1: Concept ์ ์¶
"์ถ๋ ฅ ๊ฐ๋ฅํ" ํ์ ์ ๋ํ๋ด๋ Concept์ ์ ์ํ์ธ์ (operator<< ์ง์).
์ ๋ต ๋ณด๊ธฐ
template<typename T>
concept Printable = requires(std::ostream& os, T t) {
{ os << t } -> std::same_as<std::ostream&>;
};
template<Printable T>
void print(const T& value) {
std::cout << value << "\n";
}
๋ฌธ์ 2: Range ํ์ดํ๋ผ์ธ¶
1๋ถํฐ 100๊น์ง ์ซ์ ์ค 3์ ๋ฐฐ์์ด๋ฉด์ 5์ ๋ฐฐ์๊ฐ ์๋ ์์ ์ ๊ณฑ ํฉ์ ๊ตฌํ์ธ์.
์ ๋ต ๋ณด๊ธฐ
#include <ranges>
#include <numeric>
#include <iostream>
int main() {
auto result = std::views::iota(1, 101)
| std::views::filter([](int x) { return x % 3 == 0 && x % 5 != 0; })
| std::views::transform([](int x) { return x * x; });
int sum = std::accumulate(result.begin(), result.end(), 0);
std::cout << "Sum: " << sum << "\n";
return 0;
}
๋ค์ ๋จ๊ณ¶
- 18_Design_Patterns.md - C++ ๋์์ธ ํจํด