본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::concepts (20)

by snowoods 2025. 9. 25.

Modern C++

 

std::concepts

 

개요

C++ Concepts는 템플릿 파라미터에 대한 제약 조건을 명시적으로 표현하는 기능입니다. 템플릿이 특정 요구사항(예: 특정 멤버 함수나 연산자를 지원)을 만족하는 타입에 대해서만 인스턴스화되도록 강제할 수 있습니다. 이를 통해 컴파일 타임에 타입 요구사항을 검증하고, 의도에 맞지 않는 타입이 사용될 경우 훨씬 명확하고 이해하기 쉬운 에러 메시지를 생성할 수 있습니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++20: concept, requires 키워드가 도입되어 Concepts 기능이 표준으로 채택되었습니다.

 

내용 설명

Concepts는 concept 키워드를 사용하여 정의하며, requires 절을 통해 템플릿 파라미터가 만족해야 할 제약 조건을 기술합니다. 여러 Concepts를 논리 연산자(&&, ||, !)로 조합하여 더 복잡한 제약 조건을 만들 수도 있습니다.

제공된 예제 코드는 세 가지 concept를 정의하고 활용합니다.

  1. Addable: + 연산자를 사용하여 같은 타입의 두 객체를 더할 수 있는지 검사합니다.
  2. NonNumeric: std::integralstd::floating_point 타입 특성(type traits)을 사용하여 해당 타입이 정수나 부동소수점 숫자가 아닌지 검사합니다.
  3. Concatenable: AddableNonNumericconcept&& 연산자로 조합하여, + 연산이 가능하면서도 숫자 타입이 아닌 경우로 제약 조건을 정의합니다. 즉, '연결' 가능한 타입을 의도합니다.

concat 함수는 Concatenable concept를 만족하는 타입 T에 대해서만 인스턴스화됩니다. 따라서 std::string과 같이 + 연산자로 문자열 연결이 가능하고 숫자 타입이 아닌 경우에만 concat 함수를 사용할 수 있습니다.

 

예제 코드

#include <concepts>
#include <iostream>
#include <string>

template <typename T>
concept Addable = requires(T x)
{
    x + x;
};

template <typename T>
concept NonNumeric = !std::integral<T> && !std::floating_point<T>;

template <typename T>
concept Concatenable = Addable<T> && NonNumeric<T>;

template <typename T>
requires Concatenable<T> T concat(T first, T second)
{
    return first + second;
}

int main()
{
    const auto s1 = std::string{"Hel"};
    const auto s2 = std::string{"lo"};

    std::cout << concat(s1, s2) << std::endl;
    // std::cout << concat(1, 2) << std::endl; // 컴파일 에러 발생

    return 0;
}

 

실행 결과

Hello
  • concat(s1, s2): std::string+ 연산이 가능하고(Addable 만족) 숫자 타입이 아니므로(NonNumeric 만족), Concatenable 제약 조건을 통과하여 정상적으로 "Hello"를 출력합니다.
  • concat(1, 2): int 타입은 + 연산이 가능하지만(Addable 만족), std::integral<int>true이므로 NonNumeric 제약 조건을 만족하지 못합니다. 따라서 Concatenable 제약 조건에 위배되어 컴파일 에러가 발생합니다. Concepts 덕분에 에러 메시지가 "템플릿 인스턴스화 실패"와 같은 복잡한 내용 대신 "제약 조건 불일치"로 명확하게 표시됩니다.

 

활용팁

  • 가독성 향상: 템플릿 인터페이스가 어떤 타입을 요구하는지 코드만으로 명확하게 알 수 있습니다.
  • 컴파일 에러 개선: SFINAE(Substitution Failure Is Not An Error)와 비교하여 훨씬 직관적이고 이해하기 쉬운 컴파일 에러 메시지를 제공합니다.
  • 오버로딩: concept를 사용하여 특정 타입 그룹에 대한 함수 오버로딩을 더 쉽게 구현할 수 있습니다.
  • 표준 라이브러리 활용: C++20 표준 라이브러리의 많은 부분(예: Ranges)이 Concepts를 기반으로 재설계되어 더욱 안전하고 효율적인 코드를 작성할 수 있습니다.