1. 템플릿
사용자가 원하는 타입을 지정하면 해당 타입의 형태의 물건이 나옵니다.
데이터 형식이나 함수의 일반적인 모양을 정의하는데 사용하는데 템플릿은 코드 재사용성을 증가시킵니다.
일반적으로 컴파일 타임에 타입 안정성을 보장합니다. ⇒ 컴파일 타임에 수행됩니다.
2. 함수 템플릿
#include <iostream>
#include <string>
template <typename T>
T max(T& a, T& b) {
return a > b ? a : b;
}
int main() {
int a = 1, b = 2;
std::cout << "Max (" << a << "," << b << ") ? : " << max(a, b) << std::endl;
std::string s = "hello", t = "world";
std::cout << "Max (" << s << "," << t << ") ? : " << max(s, t) << std::endl;
}
템플릿 함수도 마찬가지로 함수의 일반적인 버전을 제공하며, 호출 시 컴파일러가 타입을 결정합니다.
함수의 일반화된 선언을 의미합니다.
- 예시 Swap
template <typename T>
void Swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
함수 템플릿의 인스턴스화
함수 템플릿이 각각의 타입에 대해 처음으로 호출될 때 C++ 컴파일러는 해당 타입의 인스턴스를 생성합니다.
이렇게 생성된 인스턴스는 해당 타입에 대해 특수화된 템플릿 함수입니다.
이 인스턴스는 함수 템플릿에 해당 타입이 사용될 때마다 호출됩니다.
명시적 특수화
// 원형
template <typename T>
void Swap(T& a, T& b);
template <typename T> void Swap(T& a, T& b);
// double형
template <> void Swap<double>(double&, double&) { ... };
// 원형
template <typename A, typename B, typename C>
void test(A a, B b, C c)
// 명시적 특수화
template <typename B>
void test(int a, B b, double c)
타입이 아닌 템플릿 인자
#include <iostream>
template <typename T, int num>
T add_num(T t) {
return t + num;
}
int main() {
int x = 3;
std::cout << "x : " << add_num<int, 5>(x) << std::endl;
}
템플릿 인자로 타입만 받을 수 있는 것이 아니라 다음과 같이도 사용이 가능합니다.
int num을 받고 해당 num 변수를 이용할 수도 있습니다.
디폴트 템플릿 인자
#include <iostream>
template <typename T, int num = 5>
T add_num(T t) {
return t + num;
}
int main() {
int x = 3;
std::cout << "x : " << add_num(x) << std::endl;
}
num 값에 default로 5 값을 넣어줄 수도 있습니다.
3. 클래스 템플릿
템플릿 정의
template <typename T>
class SampleTemplate
{
public:
T getData() const;
void setData(T value);
}
템플릿 명시적 특수화
일부 경우에 대해서 따로 처리하는 것을 템플릿 특수화라고 정의합니다.
// 일반 type A, type B, type C일때 동작
template <typename A, typename B, typename C>
class test {};
// A가 int이고, type B, C가 duoble일때 동작
template <typename B>
class test<int, B, double> {};
세가지 타입을 받는 원형과, A가 int, C가 double인 경우에만 작동하는 특수한 경우를 제작할 수 있습니다.
이러한 경우를 템플릿 특수화라고 합니다.
template <>
class Vector<bool> {
... // 원하는 코드
}
유의점은 전달하는 템플릿 인자가 없더라도 특수화 하려면 template<> 라도 남겨줘야 합니다.
타입이 아닌 템플릿 인자
#include<iostream>
template <int n, int start>
class Decimal
{
public:
static const bool value = n % start == 0 ? false
: (n < start * start) ? true
: Decimal<n, start + 1>::value;
};
template <int n>
class Decimal<n, n> {
public:
static const bool value = false; // 종료 조건
};
template<>
class Decimal<2, 2>
{
public:
static const bool value = false;
};
template<>
class Decimal<1, 2>
{
public:
static const bool value = false;
};
int main()
{
const char* isPrime = Decimal<43, 2>::value == 0 ? "false" : "true";
std::cout << isPrime << std::endl;
return 0;
}
템플릿 인자로 타입만 받을 수 있는 것이 아니라 다음과 같이도 사용이 가능합니다. int start를 받고 해당 start 변수를 이용할 수도 있습니다.
디폴트 템플릿 인자
#include<iostream>
template <int n, int start = 2>
class Decimal
{
public:
static const bool value = n % start == 0 ? false
: (n < start * start) ? true
: Decimal<n, start + 1>::value;
};
template <int n>
class Decimal<n, n> {
public:
static const bool value = false; // 종료 조건
};
template<>
class Decimal<2>
{
public:
static const bool value = false;
};
template<>
class Decimal<1>
{
public:
static const bool value = false;
};
class Decimal_isPrime
{
public:
bool isPrime(int n, int start)
{
if (n <= 2)
{
return false;
}
if (start * start > n)
{
return true;
}
if (n % (start) == 0)
{
return false;
}
return isPrime(n, start + 1);
}
};
int main()
{
const char* isPrime = Decimal<43>::value == 0 ? "false" : "true";
std::cout << isPrime << std::endl;
return 0;
}
템플릿 인자로 타입대신 받은 int start에 디폴트 값을 넣어줄 수도 있습니다.
템플릿 상속
template <typename T>
class Base {
// ...
};
template <typename T>
class Derived : public Base<T> {
// ...
};
템플릿 클래스를 기반 클래스로 사용할 수도 있습니다.
4. 가변 길이 템플릿
#include <iostream>
template <typename T>
void print(T arg) {
std::cout << arg << std::endl;
}
template <typename T, typename... Types>
void print(T arg, Types... args) {
std::cout << arg << ", ";
print(args...);
}
int main() {
print(1, 3.1, "abc");
print(1, 2, 3, 4, 5, 6, 7);
}
Types... args type name 뒤에 …으로 오는 것을 템플릿 파라미터 팩(parameter pack) 이라고 부릅니다.
함수에 인자로 …로 오는 것은 함수 파라미터 팩이라고 부르며, 0개 이상의 함수 인자를 나타냅니다.
둘의 차이점은 템플릿의 경우 타입 앞에 ... 이 오고 함수의 경우 타입 뒤에 ... 이 옵니다.
print(1, 3.1, "abc");
--------------------------
template <typename T, typename... Types>
void print(T arg, Types... args) {
std::cout << arg << ", ";
print(args...);
}
여기서 타입 T 에는 int 로 추론되고 args…에는 남은 3.1 과 abc 가 들어갑니다. 그리고 아래와 같이 유추 됩니다.
void print(int arg, double arg2, const char* arg3)
{
std::cout << arg << ", ";
print(arg2, arg3);
}
-----------------------------------------
void print(double arg, const char* arg2)
{
std::cout << arg << ", ";
print(arg2);
}
-----------------------------------------
void print(const char* arg) {
std::cout << arg << std::endl;
}
위의 함수는 print를 재귀적으로 반복할 것입니다.
- 주의사항
#include <iostream>
template <typename T, typename... Types>
void print(T arg, Types... args) {
std::cout << arg << ", ";
print(args...);
}
template <typename T>
void print(T arg) {
std::cout << arg << std::endl;
}
int main() {
print(1, 3.1, "abc");
print(1, 2, 3, 4, 5, 6, 7);
}
print 함수의 위치를 변경하면 위의 가변 길이 템플릿에서는 아래 print 함수의 위치를 찾을 수가 없습니다.
그 이유는 C++ 컴파일러는 함수를 컴파일 시에, 자신의 앞에 정의되어 있는 함수들 밖에 보지 못하기 떄문입니다.
템플릿 함수는 작성 시 그 순서에 유의해서 써야 합니다.
5. 템플릿 메타 프로그래밍
template <int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0> {
static const int value = 1;
};
템플릿을 사용하여 컴파일 타임에 계산되는 값을 생성할 수 있습니다.
6. 출처
씹어먹는 C++ - <9 - 1. 코드를 찍어내는 틀 - C++ 템플릿(template)>
모두의 코드 씹어먹는 C++ - <9 - 1. 코드를 찍어내는 틀 - C++ 템플릿(template)> 작성일 : 2017-04-07 이 글은 79274 번 읽혔습니다. 에 대해서 배웁니다. 안녕하세요 여러분! 지난번 강좌 생각해보기는 잘
modoocode.com
'C++' 카테고리의 다른 글
C++ 동적 할당 (0) | 2024.12.14 |
---|---|
C++ 자료구조 구현 이중 원형 연결 리스트 (0) | 2024.12.07 |
C++ 자료구조 구현 Red-Black Tree (0) | 2024.11.30 |
C++ 클래스 (0) | 2024.05.12 |
C++ 동적할당 (0) | 2024.05.04 |