[C++20] Concept 연구

MIN·2025년 4월 21일

CPP20

목록 보기
1/8

내 첫 블로그 글이다. 앞으로 자주 쓸 예정이다.(물론 연구가 길어지면 텀이 생길 수도 있음...)
첫 글이라 시작을 이상하게 한 것 같지만, 이제 C++20에 새로 추가된 Concept을 한번 알아보도록 하겠다.

Concept

클래스 템플릿이나 함수 템플릿의 비타입 매개변수와 템플릿 타입을 제한하는 데 사용되는 요구사항

필자는 Concept의 정의에 대해서는 솔직히 할 말이 없다. 왜냐면 필자도 정의를 아직 다 알고 있는 것이 아니기 때문이다. 그래서 계속 공부하고 연구하고 있는 중이다. 이 블로그는 누군가에게 지식을 주는 블로그가 아닌 단순히 한 인간의 연구에 대한 고뇌가 들어가 있다고 생각하는 게 편할 거다.
만약 당신이 Concept에 대해서 심층 분석 및 연구를 하고 싶다면 공식 문서인 여기를 들어가서 참고하길 바란다. 필자는 Concept를 처음 접했을 때 왜 사용하지? 라는 생각을 했다. 그 부분에 대해서 설명해 보겠다.

Concept는 왜 사용할까?

우리가 C++로 코딩을 하다 보면 템플릿을 사용하게 된다. 아마 템플릿을 처음 배운다면 신세계를 경험하게 될 것이다. 내가 굳이 데이터 타입을 안 정해줘도 컴파일러가 알아서 정해준다. 하지만 사용하다 보면 불편한 점도 발견할 것이다. 템플릿은 모든 데이터 타입을 받아준다. 이것은 장점이지만 곧 단점이기도 하다.
하나의 예시로 두 개의 정수 및 실수를 더하는 함수를 만들어본다고 해보자.

template<class T>
T Test(T a, T b)
{
    return a + b;
}

int main()
{
	string s = "ABC";
	string s2 = "DEF";

    cout << Test(1, 3) << endl;				// 가능
    cout << Test(1.2f, 3.1f) << endl;		// 가능
    cout << Test(s, s2) << endl;			// 가능???
}

우리가 주목할 점은 string을 넣어준 함수다. 미리 정답을 말하자면 빌드가 가능하다. 우리는 숫자만을 더하고 싶지만 string 클래스에는 operator+을 지원해 준다. 그래서 문제없이 실행되는 것이다. 이러한 문제를 해결하고 싶어서 C++ 20에 Concept가 추가되었다.

문법

template < template-parameter-list >
concept concept-name attr (optional) = constraint-expression;

위 내용은 공식 문서에 있는 내용을 가져왔다. 필자가 주목한 점은 constraint-expression(제약 표현식)이다. 이 표현식의 결과는 반드시 부울값이어야 하며, 실행 시간에는 평가되지 않는다. 예를 들어 다음처럼 사용하면 된다.

template <class T>
concept TestConcept = sizeof(T) == 4;

이어서 Concept을 어떻게 사용하는 지 보자.

template <TestConcept TC>
class Widget
{
public:
    Widget()
    {
		...
    }

    TC test(TC a, TC b)
    {
        return a + b;
    }
};

위 예제를 이해한다면 Concept 개념을 다 이해한 거다. 짧게 설명하자면, T로 지정한 타입이 반드시 4바이트여야 한다는 것이다. 우리가 아는 4바이트는 int, float 등등이 있을 것이다. 그리고 다음 실행한 결과를 보자.

Widget<int> w;
Widget<float> w2;
//Widget<double> w2; 오류 발생

cout << w.test(1, 3) << endl;			// 결과 : 4
cout << w2.test(1.2f, 3.5f) << endl;	// 결과 : 4.7

4바이트인 int와 float는 빌드가 되지만, double은 8바이트이기 때문에 오류가 발생한다. 이처럼 Concept는 템플릿에 관련된 컴파일 에러를 읽기 쉽게 만들어 준다.

요구 표현식

requires (parameter-list) { requirements; }

여기에서는 requirements에 집중하자. 요구사항의 타입은 단순, 타입, 복합, 중첩 등 네 가지가 있다. 각각의 의미만 빠르게 파악하고 다음으로 넘어가자.

● 단순 요구사항 : requires로 시작하지 않는 표현식
● 타입 요구사항 : 주어진 타입이 올바른지 검증
● 복합 요구사항 : exception을 제대로 던지는지 또는 메서드가 주어진 타입에 맞게 리턴하는지 검증
● 중첩 요구사항 : 요구사항을 중첩해서 지정 가능

미리 정의된 표준 Concept

표준 라이브러리에는 미리 정의된 Concept가 다양하게 존재한다. 물론 필자는 전부 다 외우지 않고 필요한 게 있다면 문서를 찾아본다. 만약 궁금하다면 여기를 들어간다면 카테고리 별로 잘 정리되어 있다. 한 번쯤은 찾아보길 권한다. 물론 개인적인 생각이다.

Concept 사용 예시

사실 Concept을 사용하는 방식은 굉장히 다양하다. 다음 예시를 보자.

// 1) Requires Clause
template<class T>
requires std::integral<T>   // integral -> 정수만 받음
void TestConcept1(T number)
{
    cout << number << endl;
}

해당 함수에 requires을 선언해서 정수만을 받는 템플릿 함수를 만든 상황이다. 이게 가장 표준적인 형태로 템플릿 뒤에 requires을 작성한 모습을 볼 수 있다.

// 2) Trailing Requires Clause
template<class T>
void TestConcept2(T number) requires std::floating_point<T> // floating_point -> 실수만 받음
{
    cout << number << endl;
}

다음은 템플릿 뒤가 아닌 함수명 뒤에 이어서 작성하는 모습을 볼 수 있다. 이번엔 정수가 아닌 실수를 받는 템플릿 함수를 만들어 보았다.

// 3) Constrainted Template Parameter
template<std::integral T>
void TestConcept3(T number)
{
    cout << number << endl;
}

이번엔 requires을 없애고 템플릿 안에다가 직접 선언한 모습이다. 이것은 아까 예시에서 사용한 것과 같은 모습이다.

// 4) Abbreviated Function Template
void TestConcept4(std::integral auto number)
{
    cout << number << endl;
}

마지막으로 함수의 매개변수에 선언하고 auto까지 작성한 모습이다. 필자는 개인적으로 1번 방법이 가장 직관적으로 보이기 때문에 애용하는 형태이다.

// 클래스 상속 예시
class GameObject
{

};

class Knight : public GameObject
{

};

template<class T>
requires std::derived_from<T, GameObject>
void TestObj(T* obj)
{

}

참고로 이렇게 클래스의 상속 관계를 나타날 때도 사용 가능하다. derived_from은 T가 GameObject을 반드시 상속해야 된다는 뜻이다.

마무리

사실 여기에 나와 있는 Concept의 개념이 전부는 아니다. 아직 설명하지 않은 부분도 있고, 일부는 의도적으로 생략했다. 개인적으로 Concept을 자주 사용하지도 않고, 앞으로도 사용할 일이 많지는 않을 것 같아서 간략하게 정리하는 데 그쳤다. 더 궁금한 점이 있다면 관련 서적이나 공식 문서를 참고하길 바란다.

여기서 글을 마쳐도 되겠지만, 첫 블로그 글인 만큼 이 블로그에 대해서도 간단히 이야기해보고 싶다. 나는 게임 개발자, 그중에서도 게임 서버 개발자를 꿈꾸고 있다. 그래서 이 블로그는 앞으로 자연스럽게 게임 서버 관련 이야기들로 채워질 것이다. 나 역시 그렇게 되길 바라고 있다.

이 블로그는 내가 직접 고민하고 공부한 내용을 정리하는 공간이다. 단순히 정보를 모으는 것이 아니라, 실제로 이해하고 정리하는 과정을 통해 더 깊이 있는 공부를 하고 싶다. 지금 이 글을 쓰는 순간에도 그것을 실감하고 있다. 앞으로는 네트워크, 데이터베이스, 프로그래밍 패턴, 게임 서버, 언리얼, 유니티 등 다양한 주제를 중심으로 내가 연구하고 공부한 내용을 기록할 예정이다.

혹시 글을 읽으시면서 내용적으로 부족하거나 잘못된 부분이 있다면, 편하게 댓글로 알려주시면 감사하겠습니다. 보내주신 피드백은 앞으로 더 나은 글을 쓰는 데 큰 도움이 될 것 같습니다. 끝까지 읽어주셔서 진심으로 감사드리며, 다음에는 더 유익하고 의미 있는 주제로 찾아뵙겠습니다.

참고자료

전문가를 위한 C++(개정5판) P667~P679
Inflearn [Rookiss][C++20 훑어보기]
cppreference.com
Constraints and concepts
Concepts library

profile
게임서버개발자(진)

0개의 댓글