🧠 concept란?
C++ 템플릿에 제약 조건(조건식)을 줄 수 있는 문법
📌 “이 타입은 이런 행동을 할 수 있어야 템플릿에 넣을 수 있다”
✅ 안 되면 컴파일 에러를 깔끔하게 내줌 (더 이상 무시무시한 템플릿 에러 아님!)
🧩 예시 없이 설명 못 하지!
#include <concepts>
#include <iostream>
template <typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
};
template <Addable T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(3, 4) << '\n'; // OK
// std::cout << add("hi", "there") << '\n'; // 컴파일 에러!
}
🔍 위 예제 해석:
- Addable은 concept 이름
- requires(...) 안에 조건이 있음
- T 타입은 + 연산이 가능해야 하고
- 결과도 T로 변환 가능해야 함
- add 함수는 오직 Addable 타입만 받을 수 있음
💥 concept을 안 쓰면 이런 불편함이 있어…
template<typename T>
T add(T a, T b) {
return a + b;
}
- 타입 T가 +를 지원하지 않으면?
- 👉 컴파일 에러 메시지가 10줄~100줄 나오는 템플릿 지옥!
✅ concept을 쓰면?
- 컴파일 에러 메시지가 짧고 명확해짐
- 코드 가독성 높아짐
- 템플릿 제약 조건을 구조적으로 표현 가능
🔧 다양한 concept 종류
C++ 표준 라이브러리에 이미 많이 정의돼 있어:
concept 이름 | 의미 |
std::integral | 정수형 타입 |
std::floating_point | 실수형 타입 |
std::same_as<T> | 정확히 타입이 T일 때 |
std::derived_from<T> | T를 상속받은 타입 |
std::default_initializable | 기본 생성 가능 |
std::convertible_to<A, B> | A가 B로 변환 가능 |
🧪 사용 예시
1. 함수 파라미터 제한
template <std::integral T>
T multiply_by_two(T x) {
return x * 2;
}
2. requires 키워드 사용
template<typename T>
requires std::floating_point<T>
T square_root(T x) {
return sqrt(x);
}
3. 함수 선언 후 requires 추가
template<typename T>
T square(T x) requires std::integral<T> {
return x * x;
}
3.1 requires 표현식 고급버전
template<typename T>
concept HasPushBack = requires(T a, typename T::value_type v) {
{ a.push_back(v) };
};
// T는 push_back 함수가 있고
// 그 인자로는 value_type을 넣을 수 있어야 함
// 예: std::vector, std::list는 OK
// std::map은 안 됨 (value_type 구조 다름)
🔀 4. 여러 concept 조합
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template<Number T>
T half(T x) {
return x / 2;
}
// int, float OK
// std::string ❌ 에러
📦 5. 클래스 템플릿에 concept 적용
template<std::integral T>
class MyCounter {
public:
void increment() {
++value;
}
private:
T value = 0;
};
// MyCounter<int> OK
// MyCounter<std::string> ❌ 컴파일 에러
💎 6. concept + requires + trailing return type
template<typename T>
requires requires(T a) {
{ a.size() } -> std::convertible_to<std::size_t>;
}
auto get_size(const T& t) -> std::size_t {
return t.size();
}
// T는 size() 멤버 함수를 가져야 하고,
// 그 결과가 std::size_t로 변환 가능해야만 OK
🧪 복합 예제
#include <concepts>
#include <vector>
#include <list>
template<typename T>
concept SequenceContainer = requires(T a, typename T::value_type v) {
{ a.begin() } -> std::same_as<typename T::iterator>;
{ a.end() } -> std::same_as<typename T::iterator>;
{ a.push_back(v) };
typename T::value_type;
typename T::iterator;
};
template<SequenceContainer T>
void fill_with_42(T& container) {
for (int i = 0; i < 5; ++i) {
container.push_back(42);
}
}
void main()
{
std::vector<int> v;
fill_with_42(v); // OK
std::list<int> l;
fill_with_42(l); // OK
std::map<int, int> m;
// fill_with_42(m); // ❌ 컴파일 에러
}
🚀 언제 쓰면 좋을까?
- 템플릿 인자가 어떤 **기능(연산자, 멤버 등)**을 꼭 지원해야 할 때
- 추상적 타입 설계 시 타입 안정성 높이기
- SFINAE (enable_if)보다 훨씬 읽기 쉽고 유지보수에 유리
🤯 비교: enable_if vs concept
항목 | enable_if (C++11~17) | concept (C++20) |
문법 | 복잡하고 읽기 어려움 | 명확하고 선언적 |
에러 메시지 | 길고 추적 어려움 | 간결하고 의미 있음 |
사용성 | 제한적 | 강력하고 유연함 |
📌 요약
키포인트 | 설명 |
개념 | 템플릿 타입의 조건을 선언적으로 표현 |
키워드 | concept, requires |
효과 | 타입 제약 + 에러 개선 + 가독성 향상 |
대체 가능 기술 | enable_if, SFINAE |
📚 reference
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 function dispatch based on properties of types. These concepts provide a foundation for equation
en.cppreference.com
반응형
'프로그래밍 > C,C++' 카테고리의 다른 글
[Modern C++20] 🚀주요 특징 & 설명 (0) | 2025.04.16 |
---|---|
[Modern C++20] coroutine (0) | 2025.04.11 |
[Modern C++] string_view (0) | 2025.04.02 |
[Modern C++20] 삼중 비교 연산자(Three-Way Comparison) (0) | 2025.03.28 |
[Modern C++] 람다 표현식 (Lambda Expression) (0) | 2025.03.26 |