코딩 학습/C와 C++

C++ 기초 - 템플릿 프로그래밍

이개 2026. 4. 8. 14:01

템플릿 사용법

// 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;
}

 

 

자료구조를 구현할 때가 아니라면 클래스를 나누는 것이 낫다.