C & C++/C++
[C++] C++ 20
거북이 코딩
2024. 8. 26. 15:34
Ch 10. C++ 20
Modules
// module은 include 의 많은 단점들을 보완함
// - 매번 헤더를 소스 파일에 복사/붙여넣기 하는 것
// - pragam, ifndef 등을 사용하지 않게 됨
// - 헤더 내부에서 의도하지 않은 것들에 대한 공개
사용자 정의 모듈
export module moduleName;
export void swap(int& a, int& b)
{
}
main.cpp
import moduleName;
int num0 = 10, num1 = 20;
swap(num0, num1);
Concepts
템플릿 파라미터에 제약을 거는 것
template<typename T>
concept NonPointer = !std::is_pointer<T>::value;
// concept 을 이용하면 template 에 대한 제약을 쉽게 걸 수 있다
// 다양한 방식의 concept 적용
template<NonPointer T> // 포인터가 들어오면 컴파일이 안됨.
void swap0(T& x, T& y)
{
}
template<typename T> requires NonPointer<T>
void swap1(T& x, T& y)
{
}
template<typename T>
void swap2(T& x, T& y) requires NonPointer<T> // 함수 이름 옆에
{
}
scoped enum인지 확인하려면
template<typename T>
struct is_scoped_enum
{
static const bool value = std::is_enum_v<T> && !std::is_convertible_v<T, int>;
};
template<typename T>
concept ScopedEnum = is_scoped_enum<T>::value;
template<ScopedEnum T> // enable_if 를 사용하지 않고 매우 쉽게 제약
std::ostream& operator<<(std::ostream& os, const T& t)
{
return (std::cout << static_cast<std::underlying_type_t<T>>(t));
}
다양한 컨셉 활용
// 다양한 concept 활용법
template<typename T>
concept HasNum = requires // 중괄호가 컴파일 되는지 평가
{
T::num;
};
template<typename T>
concept HasType = requires
{
typename T::type;
};
template<typename T>
concept HasNumAndType = HasNum<T> && HasType<T>;
template<typename T>
concept HasNumOrType = HasNum<T> || HasType<T>;
template<typename T, typename S>
concept Addable = requires(T t, S s)
{
t + s;
}
&& std::is_integral_v<T> // 정수형태
&& std::is_integral_v<S>;
Ranges
이터레이터와 컨테이너를 추상화시켜서 만든 것
// sort 비교
std::vector<int> v{ 3, 2, 1 };
std::sort(v.begin(), v.end());
ranges::sort(v); // 간단하다.
// find 비교
std::vector<int> v(10);
std::iota(v.begin(), v.end(), 0); // 0 1 2 ...
auto iter = ranges::find(v, 3); // 간단하다.
std::cout << *iter << std::endl; // 3
iter == std::find(v.begin(), v.end(), 3);
std::cout << *iter << std::endl; // 3
std::cout << std::endl;
// view, lazy evaluation
// 0 ~ 9까지 출력
for (int num : ranges::views::iota(0, 10))
std::cout << num << std::endl;
// 0부터 무한대
//for (int num : ranges::views::iota(0)) // 실제로 무한대까지 숫자를 미리 만들어두지 않고 필요할 때 만든다
// std::cout << num << std::endl;
std::cout << std::endl;
// 합성, 파이프를 이용하여 합성
auto r =
ranges::views::iota(0, 10) |
ranges::views::reverse |
ranges::views::filter([](const int& num) {
return num % 2 == 0;
}) |
ranges::views::cycle |
ranges::views::take(20);
for (int num : r) {
std::cout << num << std::endl;
}
std::cout << std::endl;
// Action, 컨테이너에 영향을 줌
auto v = ranges::views::iota(0, 20) | ranges::to<std::vector<int>>();
v |= ranges::actions::reverse | ranges::actions::drop(2);
std::cout << ranges::views::all(v);
Coroutines
함수를 중간에 정지하고 돌아올 수 있는 특별한 함수이다.
std::experimental::generator<int> gen()
{
std::cout << "gen0" << std::endl;
co_yield 0;
std::cout << "gen1" << std::endl;
co_yield 1;
std::cout << "gen2" << std::endl;
co_yield 2;
std::cout << "gen3" << std::endl;
co_yield 3;
std::cout << "gen4" << std::endl;
co_yield 4;
}
int main()
{
// generator 동작 1
auto g = gen();
std::cout << "main" << std::endl;
auto iter = g.begin();
std::cout << "main0" << std::endl;
std::cout << *iter << std::endl;
++iter;
std::cout << "main1" << std::endl;
std::cout << *iter << std::endl;
++iter;
std::cout << "main2" << std::endl;
std::cout << *iter << std::endl;
++iter;
// generator 동작 2
for (auto num : gen(0, 10))
{
std::cout << num << std::endl;
}
}
int longTimeJob()
{
std::this_thread::sleep_for(2s);
return 100;
}
std::future<int> coroutine()
{
std::cout << "coroutine0" << std::endl;
int result = co_await std::async(longTimeJob);
std::cout << "coroutine1" << std::endl;
co_return result;
}
// coroutine
auto future = coroutine();
std::cout << "main0" << std::endl;
std::cout << future.get() << std::endl;