Concept 개념

  • Concept: 타입이 가져야 할 요구 조건의 집합.
  • 작성 방법: 요구조건을 코드로 작성해 타입을 제한.
  • 활용: 컴파일 시간 타입 조사 → if constexpr.

예시: Container Concept

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

Requires Expression

  • 개념: 타입이 가져야 하는 제약 조건을 표현.
  • 활용: 컴파일 시간에 조건을 평가 → bool 타입 prvalue 생성 → constexpr에도 활용 가능.

문법

  template<typename T>
concept C = requires(T a, T b)
{
    a + b;                               // simple requirement
    a.f1();                              // simple requirement
    typename T::value_type;              // type requirement
    { a + b } noexcept -> std::same_as<int>; // compound requirement
    requires sizeof(T) > 4;              // nested requirement
};
  

Compound Requirement

  • 형태: { expression } noexcept -> return_type;
  • 목적: 표현식의 반환 타입을 명시하고, noexcept 여부까지 검사.

예시

  template<typename T>
concept C1 = requires(T a, T b)
{
    { a + b } -> std::same_as<int*>;      // pointer 반환
    { a.f1() } -> std::same_as<int>;      // 함수 f1()의 반환이 int
    { b.f2() } -> std::convertible_to<bool>; // 함수 f2()의 반환이 bool로 변환 가능
};
  

Nested Requirement

  • 활용: 내부에서 추가 제약 조건을 설정.

예시

  template<typename T>
concept C1 = requires(T a)
{
    requires std::same_as<std::remove_reference_t<decltype(*a)>, std::remove_pointer_t<T>>;
};
  

Requires Clauses

  • 목적: 함수나 클래스가 특정 조건을 만족할 때만 인스턴스화.
  • 형태: template<typename T> requires Concept<T>

예시: Addable Concept

  • +연산이 없는 애들은 add 함수 자체가 생성될 수 없도록 제한
  template<typename T>
concept addable = requires(T a, T b)
{
    a + b;
};

template<typename T> requires addable<T>
T add(const T& a, const T& b)
{
    return a + b;
}
  

예시 2: 멤버 함수 제한

  • 조건을 만족하는 경우만 멤버함수 제공
  template<typename T>
class Object
{
public:
    void f1() { }
    void f2() requires std::integral<T> { }
};
  
  template<typename T>
concept F2 = requires { typename T::i_want_f2; };

template<typename T>
class Base
{
public:
    void f1() { }
    void f2() requires F2<T> { }
};

class D1 : public Base<D1> { };
class D2 : public Base<D2> { public: using i_want_f2 = void; };
  

조건에 따른 함수 템플릿 선택

  • 원칙: 더 많은 조건을 만족하는 함수가 우선 선택됨.
  #include <type_traits>
#include <print>

template<typename T> concept C1 = std::convertible_to<T, bool>;
template<typename T> concept C2 = sizeof(T) > 2;
template<typename T> concept C3 = std::integral<T>;

template<typename T> concept C4 = C1<T> && C2<T>;
template<typename T> concept C5 = C1<T> && C2<T> && C3<T>;

template<typename T> requires C1<T>
void f1(T a) { std::println("f1-C1"); }

template<typename T> requires C4<T>
void f1(T a) { std::println("f1-C4"); }

template<typename T> requires C5<T>
void f1(T a) { std::println("f1-C5"); }

int main()
{
    f1(10);   // C5
    f1(3.4);  // C4
    f1('A');  // C1
}