On this page
article
C++ Concepts
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
}