템플릿 사용법
// Main.cpp
#include <iostream>
template<typename T>
T Add(T InA, T InB)
{
return InA + InB;
}
int main()
{
std::cout << Add<int>(1, 2) << std::endl;
std::cout << Add<float>(1.f, 2.f) << std::endl;
return 0;
}
템플릿을 인스턴스화 할 때마다 컴파일러가 내부적으로 코드를 생성함.
// Main.cpp
#include <iostream>
template<typename T> // T에 자료형 인자(int, float, ...)를 전달할 때마다 템플릿 인스턴스(코드)가 생성됨.
T Add(T InA, T InB)
{
return InA + InB;
}
int main()
{
std::cout << Add<int>(1, 2) << std::endl; // int 자료형 템플릿 인스턴스(코드) 생성 요청.
std::cout << Add<float>(1.f, 2.f) << std::endl; // float 자료형 템플릿 인스턴스(코드) 생성 요청.
return 0;
}
// Main.cpp
#include <iostream>
int Add(int InA, int InB) // int 자료형 템플릿 인스턴스(코드) 생성.
{
return InA + InB;
}
float Add(float InA, float InB) // float 자료형 템플릿 인스턴스(코드) 생성.
{
return InA + InB;
}
int main()
{
std::cout << Add(1, 2) << std::endl;
std::cout << Add(1.f, 2.f) << std::endl;
return 0;
}
템플릿에 넣는 자료형 경우의 수가 많아지면 그만큼 코드가 많아지고, 코드가 많아지면 exe파일 크기도 증가하므로 필요에 따라 만들어야한다.
함수 템플릿 문법
// template <typename T> <함수선언>;
// template <class T> <함수선언>;
template <typename T>
T Add(T InA, T InB)
{
// ...
}
template <class T>
T Add(T InA, T InB)
{
// ...
}
typename과 class의 차이는 없다고 보면 된다. typename이 더 보편적이다.
함수 템플릿 호출 시에 템플릿 인자를 생략 가능하다.
Add<int>(3, 10);
Add(3, 10); // 자료형을 추측해서 코드를 만들어 줍니다.
클래스 템플릿
// MyArray.h
#pragma once
enum
{
MAX = 1024
};
template <typename T>
class MyArray
{
public:
MyArray() // 템플릿 클래스는 생성자 또한 헤더 파일에 정의해줘야 합니다.
: Capacity(static_cast<size_t>(MAX))
, Size(0)
{
}
inline size_t GetCapacity() const
{
return Capacity;
}
inline size_t GetSize() const
{
return Size;
}
inline T GetData(size_t InIndex) const
{
return Data[InIndex];
}
inline void SetData(size_t InIndex, T InData)
{
Data[InIndex] = InData;
}
bool Add(T InData) // 방법 01. 클래스 내에 정의.
{
if (Size < Capacity)
{
Data[Size] = InData;
++Size;
return true;
}
return false;
}
private:
T Data[MAX];
size_t Capacity;
size_t Size;
};
// 방법 02. 클래스 외부이지만 헤더파일에 정의.
/*
template<typename T>
bool MyArray<T>::Add(T InData)
{
if (Size < Capacity)
{
Data[Size] = InData;
++Size;
return true;
}
return false;
}
*/
템플릿 함수는 헤더에 작성해 줘야한다.
클래스 안에서 정의하거나, 헤더 안이면서 클래스 외부에다 정의해 줄 수 있다.
// MyArray.cpp
#include "MyArray.h"
// Main.cpp
#include <iostream>
#include "MyArray.h"
int main()
{
MyArray<int> IntArray;
IntArray.Add(11);
IntArray.Add(22);
IntArray.Add(33);
IntArray.Add(44);
IntArray.Add(55);
IntArray.Add(66);
IntArray.Add(77);
return 0;
}
클래스 템플릿에서는 템플릿 인스턴스를 선언할 때 템플릿 매개변수를 명시하는 것이 필
컴파일러가 함수 템플릿처럼 인자값으로 추측할 수 없기 때문이다.
MyArray<int> Scores1; // compile.
MyArray Scores2; // compile error.
다중 템플릿 매개변수를 만들 수도 있다.
template <typename T, typename U>
void Print(const T& a, const U& b)
{
cout << a << " / " << b << endl;
}
template <typename T, typename U>
class MyClass
{
private:
T Value01;
U Value02;
};
pair 클래스 만들어보기
// MyPair.h
#pragma once
template <typename T, typename U>
class MyPair
{
public:
MyPair(const T& InFirst, const U& InSecond);
const T& GetFirst() const;
const U& GetSecond() const;
private:
T First;
U Second;
};
template<typename T, typename U>
inline MyPair<T, U>::MyPair(const T& InFirst, const U& InSecond)
: First(InFirst)
, Second(InSecond)
{
}
template<typename T, typename U>
inline const T& MyPair<T, U>::GetFirst() const
{
return First;
}
template<typename T, typename U>
inline const U& MyPair<T, U>::GetSecond() const
{
return Second;
}
템플릿 - 클래스 - 객체
템플릿을 통해 클래스와 함수를 종류별로 생성 가능하다.
템플릿 클래스의 템플릿 멤버 함수 정의는 반드시 헤더에 해주어야한다.
// Main.cpp
#include <iostream>
#include "MyArray.h"
int main()
{
MyArray<int> IntArray;
// MyArray<int> 템플릿 인스턴스가 컴파일 타임에 생성 요청됩니다.
// 해당 템플릿 인스턴스(코드)는 Main.cpp에 생성될 것입니다.
// 근데 MyArray 클래스에는 bool Add(T InData) 함수가 있고, 함수 정의는 MyArray.cpp에 있습니다.
// 컴파일 단계에서는 각 .cpp 파일이 따로따로 컴파일됩니다. 서로의 존재를 모릅니다.
// 즉, 컴파일 타임 때 Main.cpp는 MyArray.cpp에 있는 bool Add(T InData) 함수의 정의를 만들 수 없게 되고,
// bool Add(T InData) 함수의 정의가 만들어지지 않아 링커 에러가 난 것입니다.
// 즉, 템플릿 클래스의 템플릿 멤버 함수 정의는 반드시 헤더파일에 해두어야 한다는 것을 알 수 있습니다.
...
}
이때문에 수정시 재컴파일이 이루어지며, 템플릿 인스턴스화로 인해 코드 길이가 증가하게 되는 것이 단점이다.
템플릿이 연산이 안 되는 값을 받게 되면?
-> 컴파일 에러가 난다.
template <typename T>
T add(T a, T b) {
return a + b; // 여기서 '+' 연산이 가능한지 검사함
}
struct Dummy { int x; }; // '+' 연산자가 정의되지 않은 구조체
int main() {
Dummy d1, d2;
add(d1, d2); // 에러 발생! Dummy 타입은 '+' 연산을 할 수 없음
}
해결방법
SFINAE(Substitution Failure Is Not An Error) 혹은 C++ 20부터 도입된 concepts를 이용하면 예외처리를 할 수 있다.
SFINAE
- std::enable_if를 사용하여 연산이 불가능한 타입이 들어오면 아예 해당 함수 후보군에서 제외해 버린다.
- 코드가 매우 복잡해진다는 단점이 있다.
concepts
#include <concepts>
template <typename T>
requires std::integral<T> || std::floating_point<T> // 정수나 실수만 허용
T add(T a, T b) {
return a + b;
}
자료구조를 구현할 때가 아니라면 클래스를 나누는 것이 낫다.
'코딩 학습 > C와 C++' 카테고리의 다른 글
| C++ 기초 - 구조체 집합 초기화, 포인터 매개변수, 연속 선언, array (0) | 2026.04.10 |
|---|---|
| C++ 기초 - enum class, auto, default, delete, final, override, 람다 함수 (0) | 2026.04.10 |
| C++ 기초 - Casting, inline 키워드, static 키워드, 예외 처리 (1) | 2026.04.08 |
| C++ 기초 - string, File I/O (0) | 2026.04.08 |
| C++ 기초 - 복사 생성자와 오버로딩 (0) | 2026.04.07 |