C++/STL

[C++ 20] Concepts 2

sseram 2024. 2. 22. 16:03
반응형

2023.05.23 - [C++/STL] - [C++20] Concepts

 

[C++20] Concepts

https://en.cppreference.com/w/cpp/concepts Concepts library (since C++20) - cppreference.com The concepts library provides definitions of fundamental library concepts that can be used to perform compile-time validation of template arguments and perform fun

donot-simsim.tistory.com

 

위 글에서 한번 다루긴 했는데, 그때는 너무 수박 겉핥기식으로 해둬서...

조금 더 공부했기에 다시 올려본다.

 

C++ 20 이전에는 if constexpr, 부분 특수화 template 을 이용해 여러 가지 type에 대한 template를 나눴다면,

C++ 20부터는 concept과 requires를 이용하여 좀 더 간단하고 직관적이게 나눌 수 있게 되었다.

 

 

 


Concept

 

== named set of requirements


타입이 가져야 할 요구 조건들의 집합.
그리고 그 요구 조건들의 집합을 코드로 작성.

 

 

 

 

기본 형태는 아래와 같다.

template <typename T>
concept concept_name = constraint expression;

 

 

concept_name에는 변수같이 이름을 넣으면 되고, 뒤 constraint expression 에 여러 조건을 넣어 사용 할 수 있다.

다만 여기서 말하는 조건들은 [컴파일 시 조사] 가 가능하여야 하고, 반드시 [bool type] 이어야 한다.

 

 

위에서 말한 조건을 만족하는 concept을 

 

template <typename T>
concept C1 = true;

constexpr bool yes() {return true;}

template <typename T>
concept C2 = yes();

template <typename T>
concept C3 = sizeof(T) > 4;

template <typename T>
concept C4 = std::is_integral_v<T>;

template <typename T>
concept C5 = C1<T> || C2<T>;

template <typename T>
concept C6 = requires(T& c)
{
    c.begin();
};

 

 

위처럼.

 

그리고 concept에 바로 type을 집어넣어, 해당 type이 concept에 맞는지 조사할 수 있다.

 

int main() {
    std::cout << C1<double> << std::endl;	// true
    std::cout << C2<double> << std::endl;	// true
    std::cout << C3<double> << std::endl;	// true
    std::cout << C4<double> << std::endl;	// false
    std::cout << C5<double> << std::endl;	// true
    std::cout << C6<double> << std::endl;	// false
}

 

 

 

 


 

위 concept를 사용하여 함수를 하나 만들어 보자.

pointer가 와도, 일반 변수가 와도 print 를 할 수 있는 function을 만들 것이다.

#include <iostream>

template <typename T>
void pointer_print(T a) {
	// TODO
};

int main() {
	int a = 3;

	pointer_print(&a);
	pointer_print(a);
}

 

 

각각의 C++ 버전 concept에 따라 구현을 해 보자

 

 

C++14

#include <iostream>

template <typename T,
	typename std::enable_if_t<!std::is_pointer_v<T>, bool> = true>
void pointer_print(T a) {
	std::cout << a << std::endl;
}

template <typename T,
	typename std::enable_if_t<std::is_pointer_v<T>, bool> = true>
void pointer_print(T a) {
	std::cout << *a << std::endl;
};

int main() {
	int a = 3;
	
	pointer_print(&a);
	pointer_print(a);
}

 

 

 

C++ 17

#include <iostream>

template <typename T>
void pointer_print(T a) {
	if constexpr (std::is_pointer_v<T>) {
		std::cout << *a << std::endl;
	}
	else {
		std::cout << a << std::endl;
	}
};

int main() {
	int a = 3;

	pointer_print(&a);
	pointer_print(a);
}

 

 

C++ 20

#include <iostream>
#include <concepts>

template<typename T>
concept pointer_container = std::is_pointer_v<T>;

template <typename T>
void pointer_print(T a) {
	std::cout << a << std::endl;
};

template <typename T>
requires pointer_container<T>
void pointer_print(T a) {
	std::cout << *a << std::endl;
};

int main() {
	int a = 3;
	
	pointer_print(&a);
	pointer_print(a);
}

 

 

 

이렇게만 보면

 

잉? if constexpr 쓰는게 가장 편하게 쓸 수 있지 않나? 라고 생각한다.

 

하지만 concept의 진가는 requires와 함께, 좀 더 복잡한 확인이 들어갈 떄 발휘한다.

 

 

 

requires는 다음 글에서..

 

 

 

 

 

* 잘못된 정보는 덧글로 알려주시면 감사한 마음으로 수정하겠습니다.

반응형