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;