여러 함수를 만들어내는 함수의 틀
일반적으로 함수를 호출할때, 함수(인자,인자); 이런식으로 호출을 한다
만약 함수의 오버로딩을 사용한다면?
함수의 오버로딩 : 함수의 이름은 같지만, 들어오는 매개변수의 타입이 다른 것
ex) 함수의 오버로딩
메인함수쪽에서 Print()함수를 호출하고 있다
-> 클라이언트(호출하는 쪽)
함수의 오버로딩 자체가 함수를 호출하는 쪽에서 매개변수의 타입을 미리 알고있다는 전제로 만들어진다
-> 클라이언트가 함수의 매개변수 타입을 알고 그 타입에 맞는 값을 인자로 넣는다
그렇기 때문에 만약 클라이언트에서 사용자 정의 타입(클래스)의 타입을 결정해야 할 경우 함수의 오버로딩은 사용할 수 없다
이것이 템플릿을 사용하는 이유이다!
템플릿 함수를 사용하면 컴파일러는 클라이언트 (호출하는 쪽)의 함수 호출 인자 타입을 보고 템플릿 함수의 매개변수 타입을 결정한다
#include <iostream>
using namespace std;
void Print(int a, int b)
{
cout << a << "," << b << endl;
}
void Print(float a, float b)
{
cout << a << "," << b << endl;
}
void Print(char a, char b)
{
cout << a << "," << b << endl;
}
int main()
{
Print(1, 2);
Print(1.5f, 4.5f);
Print('A', 'B');
}
출력값 :
함수 앞에 template <typename T> 키워드를 붙인다
T는 템플릿 매개변수이며 함수를 호출하는 쪽에서 결정한다 (자료형)
함수 템플릿 인스턴스 : 컴파일러가 생성한 함수 정의 코드
-> 호출하는 타입을 매개변수로 하는 함수
타입에는 자료형 뿐 아니라 정수값도 들어올 수 있다
-> 명시적 호출로만 가능하다
#include <iostream>
using namespace std;
template <typename T>
void Print(T a, T b)
{
cout << a << "," << b << endl;
}
int main()
{
Print(1, 2);
Print(1.5f, 4.5f);
Print('A', 'B');
}
출력값 :
Print<int>(), Print<float>(), Print<char>() 함수가 만들어지고 함수 템플릿은 존재하지 않게됨
template <typename T1, typename T2> 키워드 붙이기
'특정 타입'에 대해 다른 동작을 정의하고 싶을때 사용한다
ex) 만약 사용자 정의 타입의 연산을 하고싶을때
아래 코드와 같이 사용자 정의 타입(클래스) 객체끼리의 연산을 하려면 연산자 오버로딩이 필요하다!
컴파일러는 C++에서 정의된 연산만 가능하기 때문에, 클래스는 사용자가 정의해둔 것이기 때문에 연산이 불가능하다
클래스 내부에 연산자 오버로딩을 사용하여 선언해주면 두 객체의 연산이 가능해진다
오버로딩은 따로 정리해둔 블로그를 다시 보도록 하자
https://velog.io/@yangju058/STL-연산자-오버로딩


#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b)
{
return a + b;
}
class MyClass
{
public:
int value;
MyClass(int v)
{
this->value = v;
}
MyClass operator +(const MyClass& other)
{
return MyClass(value + other.value);
}
};
int main()
{
MyClass obj1(10), obj2(20);
MyClass result = obj1 + obj2;
cout << result.value << endl;
}
함수 위에 template<> 키워드 사용
매개변수로 사용자 정의타입을 받는다
안쪽에 사용자 정의 타입의 멤버 변수끼리의 연산 결과를 반환한다
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b)
{
return a + b;
}
// MyClass에 대한 템플릿 특수화
template <>
MyClass add(MyClass a, MyClass b)
{
return MyClass(a.value + b.value); // 명시적으로 더하기 연산 정의
}
class MyClass
{
public:
int value;
MyClass(int v)
{
this->value = v;
}
};
int main()
{
std::cout << add(3, 5) << std::endl; // 일반 템플릿 사용
MyClass obj1(10), obj2(20);
MyClass result = add(obj1, obj2); // 특수화된 함수 호출
std::cout << "MyClass 결과: " << result.value << std::endl;
}
operator()로 정의해도 똑같이 객체끼리 연산할 수 있는데, 왜 템플릿 특수화를 사용할까?
템플릿을 사용하면 어떤 타입이 들어오든 일반적인 연산을 수행할 수 있다
근데, 특정 타입에서만 다르게 동작해야 한다면 템플릿 특수화를 사용하여 별도로 처리하는 것이 더 적절하다
코드의 직관성을 높이고, 가독성이 좋기 때문이다
여러 클래스를 만들어내는 클래스의 틀
형식 : template <typename T> 키워드를 붙이고, class 클래스 이름
앞에서 본 함수 템플릿과 거의 같다
템플릿 매개변수 인자를 통해 클라이언트가 클래스에 사용될 타입을 결정할 수 있다
클래스 템플릿도 마찬가지로, 컴파일러가 클래스 템플릿을 이용하여 실제 클래스(자료형 지정된걸로)를 생성한다
-> 기존 클래스 템플릿은 없어지고, 클래스 템플릿 인스턴스가 생성된다

#include <iostream>
using namespace std;
template <typename T1 = int, typename T2 = int>
class Pair
{
private:
T1 first;
T2 second;
public:
Pair(T1 a, T2 b)
{
first = a;
second = b;
}
void print()
{
cout << "First: " << first << ", Second: " << second << endl;
}
};
int main()
{
Pair<int, double> p1(10, 3.14);
Pair<string, char> p2("Hello", 'A');
Pair<>p3(10, 20); // 디폴트값 사용
p1.print();
p2.print();
p3.print();
return 0;
}
사용 형식 : template <> class 클래스 이름<매개변수 타입>
#include <iostream>
#include <string>
using namespace std;
// 일반 템플릿
template <typename T>
class Printer
{
public:
void print(T value)
{
cout << "일반 템플릿: " << value << endl;
}
};
//string에 대한 클래스 템플릿 특수화
template <>
class Printer<string>
{
public:
void print(string value)
{
cout << "문자열 특수화: " << value << endl;
}
};
int main()
{
Printer<int> intPrinter;
Printer<string> stringPrinter;
intPrinter.print(42);
stringPrinter.print("Hello!"); // 특수화된 템플릿 사용
return 0;
}