Generic Programming using Template
중요 Part이다.★★★★★
◆ 클래스 템플릿 매개 변수
● 타입이 아닌 다른 매개변수를 사용할 수 있음(non-type template argument)
☞ 아래 예제에서 N의 값을 템플릿 클래스의 사용 시에 직접 명시해 줌
template <typename T, int N>
class Array {
int size = N;
T Values[N];
};
* 정적 배열이기 때문에 고정된 크기가 필요해서, 숫자 N 자체를 템플릿 인수로 같이 받도록 정의함!
int main()
{
Array<int, 5> nums;
Array<double, 10> nums2;
}
해당 코드를 보고 컴파일러는 아래처럼 작성한다.
class Array<int, 5> {
int size = 5;
int Values[5];
};
class Array<double, 10> {
int size = 10;
int Values[10];
};
📘 클래스 템플릿 매개 변수: Non-type Template Argument
🎯 템플릿의 매개변수는 꼭 타입(typename) 일 필요는 없다.
**정수형 값과 같은 타입이 아닌 인자(non-type argument)**도 사용할 수 있다.
📄 예제: 정수형 매개변수를 사용하는 클래스 템플릿
template <typename T, int N>
class Array {
private:
int size = N;
T Values[N]; // 정적 배열 (크기가 N인 배열)
};
- T는 배열의 요소 타입
- N은 배열의 크기
- Values[N]는 컴파일 타임에 크기가 고정된 정적 배열
📄 사용 예제
Array<int, 5> nums; // int형 5칸짜리 배열
Array<double, 10> nums2; // double형 10칸짜리 배열
class Array<int, 5> {
int size = 5;
int Values[5];
};
class Array<double, 10> {
int size = 10;
double Values[10];
};
📌 즉, T, N 조합에 따라 서로 완전히 다른 클래스가 만들어지는 것.
❓ 왜 non-type 템플릿 매개변수가 중요한가?
✅ 이유는 다음과 같다:
- 정적 배열 크기를 컴파일 타임에 안전하게 관리 가능
- 배열 사이즈를 템플릿으로 전달하면, 컴파일 시점에 크기가 고정됨
- 런타임 오류를 방지할 수 있음
- 코드 재사용성 증가
- 템플릿 하나로 다양한 크기의 배열을 처리할 수 있음
- 매번 클래스 새로 만들 필요 없음
- STL의 기반 구조와 동일
- std::array <T, N> 같은 STL 클래스도 이 구조 사용
- C++을 제대로 이해하려면 필수 개념
📌 특히 임베디드 시스템, 머신비전 장비처럼
동적 메모리 할당을 피하고 정적 메모리를 써야 하는 경우
이 문법은 아주 유용하다.
❓ C#의 List는 동적할당인가? 크기를 지정하지 않고 선언해도 되는 이유는?
✅ C#의 List<T>는 기본적으로 동적할당 구조다.
List<int> numbers = new List<int>(); // 초기 크기 없이 생성 가능
- 내부적으로 배열을 사용하지만, 필요할 때 자동으로 확장
- 메모리는 Heap에 동적할당됨
- C++의 vector와 비슷함
즉, 선언만 하면 내부적으로 new T []를 하고, 크기가 초과되면 배열을 복사해서 다시 할당함.
정적 메모리 기반인 C++ 배열과는 구조적으로 다르기 때문에,
C++에서는 T arr[N]처럼 컴파일 시점에 크기를 알아야 하는 구조가 필요해.
🎯 요약
- 템플릿 인자는 타입만 쓸 수 있는 게 아니다
- int N처럼 정수값도 인자로 사용 가능
- 이 기능은 STL, 특히 std::array와 같이 고정 크기 컨테이너 구현 시 필수
- 머신비전, 임베디드 개발에서 유용하며,
메모리 안정성과 성능 면에서 큰 장점이 있다
📄 예제: 2_Class_Template.cpp
🎯 설명
기본적인 클래스 템플릿 사용 예제
한 가지 타입 인자(T)를 받아서 저장하고 출력하는 박스 클래스
#include <iostream>
#include <string>
template <typename T>
class Box {
private:
T data;
public:
Box(T value) : data(value) {}
void print() const {
std::cout << "Value: " << data << std::endl;
}
T getData() const {
return data;
}
};
int main() {
Box<int> b1(42);
Box<std::string> b2("Hello, Template!");
Box<double> b3(3.1415);
b1.print(); // Value: 42
b2.print(); // Value: Hello, Template!
b3.print(); // Value: 3.1415
return 0;
}
- Box <T>는 단일 타입 인자 사용
- 다양한 타입으로 템플릿 인스턴스화 가능
📄 예제: 3_Class_Template_Array.cpp
🎯 설명
Non-type 템플릿 매개변수를 포함한 클래스 템플릿 예제
고정 크기 배열을 관리하는 템플릿 클래스
#include <iostream>
template <typename T, int N>
class Array {
private:
T data[N];
public:
void set(int index, T value) {
if (index >= 0 && index < N) {
data[index] = value;
}
}
T get(int index) const {
if (index >= 0 && index < N) {
return data[index];
}
return T(); // 기본값 반환
}
void print() const {
for (int i = 0; i < N; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
};
int main() {
Array<int, 5> arr1;
Array<double, 3> arr2;
for (int i = 0; i < 5; ++i) arr1.set(i, i + 1); // 1 2 3 4 5
arr2.set(0, 1.1);
arr2.set(1, 2.2);
arr2.set(2, 3.3);
arr1.print();
arr2.print();
return 0;
}
- Array <T, N>은 타입과 크기 둘 다 템플릿 인자로 받음
- T data[N]로 정적 배열을 구성
- STL의 std::array와 유사한 구조
Generic Programming과 템플릿
◆ Generic Programming 이란 : 타입에 관계없이 동작하는 코드 작성 개념
● Macro를 활용한 Generic Programming : 매크로를 활용하여 코드를 대처하는 방식, 주의가 필요
◆ 템플릿을 활용한 Generic Programming
● 함수 템플릿 : template <typename T>, 템플릿 인자와 특수화
● 클래스 템플릿 : 클래스에도 동일하게 사용 가능, 템플릿 인자에도 int N 등도 사용 가능함.
📘 이번 주 핵심 정리: Generic Programming과 템플릿
🎯 Generic Programming이란?
타입에 관계없이 동작하는 코드를 작성하는 프로그래밍 기법이다.
C++에서는 이를 위해 매크로 또는 템플릿 방식을 활용할 수 있다.
📌 방법 1: 매크로를 활용한 Generic Programming
- #define을 사용하여 코드 조각을 복사-붙여넣기처럼 대체함
- 예시:
#define SQUARE(a) a * a
int result = 100 / SQUARE(5); // → 100 / 5 * 5 → 100
- ❗ 단점
- 연산자 우선순위 오류 발생 가능
- 디버깅이 어렵다
- 타입 체크가 되지 않음
📌 방법 2: 템플릿을 활용한 Generic Programming
✔️ 함수 템플릿
- 타입을 T로 일반화한 함수 정의
- 컴파일 시 실제 타입에 맞게 코드가 생성됨
- 예시:
template <typename T>
T Max(T a, T b) {
return (a > b) ? a : b;
}
Max<int>(3, 5); // → int 버전 생성
Max<double>(3.2, 4.5); // → double 버전 생성
✔️ 다중 템플릿 인자
- 서로 다른 타입을 받을 수 있음
template <typename T1, typename T2>
void func(T1 a, T2 b) { ... }
✔️ 함수 템플릿 특수화
- 특정 타입에 대해 다르게 동작하도록 구현 가능
template <>
std::string Max(std::string a, std::string b) {
return (a.length() > b.length()) ? a : b;
}
📌 클래스 템플릿
- 함수 템플릿과 동일한 문법으로 클래스에도 사용 가능
- 멤버 변수나 멤버 함수에 사용되는 타입을 일반화함
template <typename T>
class Box {
private:
T data;
public:
Box(T value) : data(value) {}
T getData() const { return data; }
};
📌 클래스 템플릿의 다중 인자
- T1, T2 등 다중 타입으로 일반화 가능
- 특정 인자만 고정해서 부분 특수화도 가능
template <typename T1, typename T2>
class Item { ... };
template <typename T1>
class Item<T1, double> {
// T2가 double일 때만 이 클래스 사용됨
};
📌 Non-type 템플릿 인자 (클래스 템플릿에서)
- 템플릿 인자로 값(int) 을 전달할 수 있음
- 정적 배열처럼 컴파일 타임에 크기를 알아야 하는 구조에 적합
template <typename T, int N>
class Array {
T values[N];
};
- 사용 예:
Array<int, 5> arr1; // int[5]
Array<double, 10> arr2; // double[10]
- STL의 std::array 구현 원리와 유사
❓ 머신비전 개발 환경에서는 어떻게 활용되는가?
- 정적 메모리 사용이 많은 머신비전 분야에서는
메모리 효율과 성능이 중요하기 때문에
Non-type 템플릿과 정적 배열 구조가 자주 사용됨 - DIO, 카메라 인터페이스 등 하드웨어 제어 코드에서
템플릿으로 타입 추상화하거나 배열 크기를 고정할 수 있음 - 예:
Array<BYTE, 16>처럼 고정 크기 버퍼나
Packet<uint8_t, 64>처럼 타입 + 크기 기반의 통신 구조 설계에 활용됨
✅ 마무리
- 템플릿은 매크로보다 안전하고 강력한 방식
- 컴파일러가 타입에 따라 자동으로 코드 생성
- STL 이해를 위해 반드시 필요한 개념
- 템플릿 함수, 클래스, 특수화, non-type 인자까지 이번 주 핵심
다음 강의인 STL의 기반이 되는 내용이므로 이번 정리를 꼭 복습해두기 💙
◆ 템플릿의 특수화
● 특정 자료형에 대해서는 템플릿을 사용하지 않고 별도 구현한 함수를 사용하도록 구현 가능
● 아래 경우에는 템플릿 특수화를 하지 않아도 오버로딩을 통해 동일하게 동작
● 하지만 미묘한 차이가 있으므로 실제 사용할 때 문제가 생기면 참고
c++ - Should you prefer overloading over specialization of function templates? - Stack Overflow
template<typename T>
T min(T a, T b)
{
return (a < b) ? a : b;
}
template<>
std::string min(std::string a, std::string b)
{
return (a.length() < b.length()) ? a : b;
}
이 예제에서 특수화를 명시하지 않아도 std::string 에 대해서 동작
❓ "템플릿 특수화 안 했는데도 왜 std::string 함수가 호출되는지?"
바로 아래 코드 예제를 보자:
template <typename T>
T min(T a, T b) {
return (a < b) ? a : b;
}
template<>
std::string min(std::string a, std::string b) {
return (a.length() < b.length()) ? a : b;
}
🎯 핵심 개념 구분
구분 | 설명 | 예시 |
함수 템플릿 특수화 (specialization) | 템플릿 함수의 특정 타입에 대해 별도로 구현 | template<> std::string min(std::string, std::string) |
함수 오버로딩 (overloading) | 같은 이름의 함수지만 매개변수 타입이 다름 | std::string min(std::string, std::string) (템플릿 아님) |
💡 이 코드에서는 특수화지만, 왜 오버로딩처럼 동작하냐?
➤ 실제로는 "템플릿 특수화"처럼 보이지만, 함수 오버로딩과 매우 유사한 방식이기 때문에
컴파일러는 아래와 같이 일반 함수가 더 정확히 일치하면 그걸 우선 호출함:
std::string a = "hi";
std::string b = "hello";
min(a, b); // → 특수화된 템플릿이 아닌, 일반 함수처럼 우선 일치된 버전 호출
✅ 그럼 차이는 뭐야?
상황 | 오버로딩 | 특수화 |
함수명만 보고 매칭 가능 | O | O |
일치하는 타입이 있을 때 우선 호출 | O (가장 정확한 매개변수 타입) | △ (정확히는 템플릿 매칭 규칙 적용) |
템플릿 내부 구조를 활용 가능 | X | O |
클래스 템플릿처럼 다양한 타입 대응 | X | O |
✅ 결론
- 예시는 "템플릿 특수화"지만, 오버로딩처럼 작동 가능해 보인다.
- 둘의 차이는 미묘하지만, 템플릿 내부 로직이 필요한 경우는 특수화를 선택하고,
- 간단히 타입이 다를 때만 다르게 동작하고 싶다면 오버로딩이 더 낫다는 의견이 많다.
💬 머신비전 쪽에서는?
템플릿 특수화보다 오버로딩을 더 많이 사용한다.
왜냐하면 장비 제어 함수에서 타입에 따라 살짝 다른 처리만 필요할 때가 많고,
컴파일러가 오버로딩을 더 명확하게 처리해줘서 유지보수가 쉬운 편이다.
https://youtu.be/sMt6KRA-XGI?si=vi-nPU_dyZu_JLSq
사실 올해 STL 부터 공부했다가 아예 이해가 되지 않았는데 이제야 대략적으로 이해가 간다.
제너릭프로그래밍 아니 상속부터 다시 공부했었어야 했다.
'C++ > C++ : Study' 카테고리의 다른 글
10. STL - (1) 개요 (3) | 2025.07.07 |
---|---|
9. Generic Programming과 템플릿 - 복습 퀴즈 (0) | 2025.07.01 |
9. Generic Programming과 템플릿 (5) - 클래스 템플릿 (1) | 2025.07.01 |
9. Generic Programming과 템플릿 (4) - 템플릿 인자 & 특수화 (0) | 2025.06.30 |
9. Generic Programming과 템플릿 (3) - 함수 템플릿 (6) | 2025.06.26 |