따라하며 배우는 C++ 13. 템플릿
13.1 함수 템플릿
int getMax(int x, int y)
{
return (x > y) ? x : y;
}
double getMax(double x, double y)
{
return (x > y) ? x : y;
}
//이렇게 형이 여러 개일 경우 템플릿이 유용하다.
#include <iostream>
using namespace std;
template<typename T>
//typename 대신 class를 쓸 수도 있다.
//늬앙스가 약간 다를 뿐 거의 비슷한 기능을 한다.
T getMax(T x, T y)
{
return (x > y) ? x : y;
}
int main()
{
cout << getMax(1, 2) << endl;
cout << getMax(1.241234, 3.24234) << endl;
cout << getMax(1.2f, 3.2f) << endl;
cout << getMax('a' , 'c') << endl;
return 0;
}
컴파일러가 어떤 형이 들어갈지를 찾아내고, 실행될 때 내부적으로 함수 오버로딩처럼 작동되는 것.
T 안에 사용자 정의 클래스를 사용할 수도 있다.
템플릿으로 만든 function을 실제로 만드는 것을 instanciation이라고 부른다.
13.2 클래스 템플릿
MyArray.h
#pragma once
#include <assert.h>
#include <iostream>
template<typename T>
//T는 아무거나 사용할 수 있다. type에 대한 매개변수.
class MyArray
{
private:
int m_length;
T* m_data;
public:
MyArray()
{
m_length = 0;
m_data = nullptr;
}
MyArray(int length)
{
m_data = new T[length];
m_length = length;
}
~MyArray()
{
reset();
}
void reset()
{
delete[] m_data;
m_data = nullptr;
m_length = 0;
}
T& operator[](int index)
{
assert(index >= 0 && index < m_length);
return m_data[index];
}
int getLength()
{
return m_length;
}
void print()
{
for (int i = 0; i < m_length; ++i)
std::cout << m_data[i] << " ";
std::cout << std::endl;
}
};
Source.cpp
#include <iostream>
#include "MyArray.h"
using namespace std;
int main()
{
MyArray<double> my_array(10);
for (int i = 0; i < my_array.getLength(); ++i)
my_array[i] = i * 0.5;
my_array.print();
MyArray<double> my_array2(10);
for (int i = 0; i < my_array2.getLength(); ++i)
my_array2[i] = i + 65;
my_array2.print();
return 0;
}
#include "MyArray.h"
template<typename T>
void MyArray<T>::print()
{
for (int i = 0; i < m_length; ++i)
std::cout << m_data[i] << " ";
std::cout << std::endl;
}
//explicit instantiation
//컴파일러가 인식을 할 수 없다면
template void MyArray<char>::print();
template void MyArray<double>::print();
template void MyArray<int>::print();
//로 빌드할 수 있다.
//클래스 자체를 explicit instantiation도 가능
//template class MyArray<char>
//template class MyArray<double>
main에서는 "MyArray.h"로 include하고,
MyArray.h에 있는 함수의 body가 "MyArray.cpp"에 있는 경우 어떤 형으로 변환할지 컴파일러가 찾지 못할 수도 있다.
이럴 경우 cpp 아래에 저렇게 지정해주면 문제가 사라진다.
13.3 자료형이 아닌Non-type 템플릿 매개변수
#include <iostream>
#include "MyArray.h"
using namespace std;
int main()
{
//정적 할당, 사이즈가 컴파일 타임에 알려져 있어야 함.
MyArray<double, 100> my_array; //std::array<double, 100>
for (int i = 0; i < my_array.getLength(); ++i)
my_array[i] = i + 65;
my_array.print();
return 0;
}
MyArray.h
#pragma once
#include <assert.h>
#include <iostream>
//non-type template의 경우 형을 선언하기가 힘들기 때문에
//헤더에 무조건 집어넣는 편
//m_length를 대체하는 T_SIZE
template<typename T, unsigned int T_SIZE>
//T는 아무거나 사용할 수 있다. type에 대한 매개변수.
class MyArray
{
private:
//int m_length;
T* m_data; // T m_data[m_length]
public:
MyArray()
{
m_data = new T[T_SIZE];
}
~MyArray()
{
delete[] m_data;
}
T& operator[](int index)
{
assert(index >= 0 && index < T_SIZE);
return m_data[index];
}
int getLength()
{
return T_SIZE;
}
void print()
{
for (int i = 0; i < T_SIZE; ++i)
std::cout << m_data[i] << " ";
std::cout << std::endl;
}
};
13.4 함수 템플릿 특수화
template<typename T>
T getMax(T x, T y)
처럼 선언되어 있는 경우,
getMax(1, 2);
getMax<double>(1.0, 2.0);
처럼도 사용 가능하다.
#include <iostream>
using namespace std;
template <typename T>
T getMax(T x, T y)
{
return (x > y) ? x : y;
}
template<>
char getMax(char x, char y)
{
cout << "Warning : comparing chars" << endl;
return (x > y) ? x : y;
}
int main()
{
cout << getMax('a', 'b') << endl;
return 0;
}
또한 template이 사용되고 있는 함수에 대해, 특정 변수형에 대해선 특수하게 작동되도록 하고 싶을 때,
역으로 그 변수형에 대해 함수 템플릿을 특수화시킬 수 있다.
Source.cpp
#include <iostream>
#include "Storage.h"
int main()
{
Storage<int> nValue(5);
Storage <double> dValue(6.1);
nValue.print();
dValue.print();
return 0;
}
Storage.h
#pragma once
#include <iostream>
template <class T>
class Storage
{
private:
T m_value;
public:
Storage(T value)
{
m_value = value;
}
~Storage()
{}
void print()
{
std::cout << m_value << '\n';
}
};
template<>
void Storage<double>::print()
{
std::cout << "Double Type : ";
std::cout << std::scientific << m_value << '\n';
}
이 때 cpp 파일에 특수화시킨 걸 집어넣어 정리하는 경우
template<>
void Storage<double>::print()
{
std::cout << "Double Type : ";
std::cout << std::scientific << m_value << '\n';
}
그냥 헤더에 넣는 것이 일반적이지만,
header에도 cpp 파일을 include하는 식으로 해결할 수 있다.
13.5 클래스 템플릿 특수화
#include <iostream>
#include <array>
#include "Storage.h"
using namespace std;
template<typename T>
class A
{
public:
void doSomething()
{
cout << typeid(T).name() << endl;
}
void test() {}
};
template<>
class A<char>
{
public:
void doSomething()
{
cout << "Char type specialization" << endl;
}
};
int main()
{
A<int> a_int;
A<double> a_double;
A<char> a_char;
a_int.doSomething();
a_double.doSomething();
a_char.doSomething();
return 0;
}
//실제로는 다른 클래스를 정의하는 것과 진배 없기는 하다.
int main()
{
A<int> a_int;
A<double> a_double;
A<char> a_char;
a_int.doSomething();
a_double.doSomething();
a_char.doSomething();
a_int.test();
a_double.test();
//a_char.test(); //불가
return 0;
}
문법 상에는 아예 다른 클래스 취급된다.
단, instanciation처럼 사용할 수 있어 편리해진다.
Source.cpp
#include <iostream>
#include <array>
#include "Storage8.h"
using namespace std;
template<typename T>
class A
{
public:
void doSomething()
{
cout << typeid(T).name() << endl;
}
void test() {}
};
template<>
class A<char>
{
public:
void doSomething()
{
cout << "Char type specialization" << endl;
}
};
int main()
{
Storage8<int> intStorage;
for (int count = 0; count < 8; ++count)
intStorage.set(count, count);
for (int count = 0; count < 8; ++count)
std::cout << intStorage.get(count) << std::endl;
cout << "Size of Storage8<int> " << sizeof(Storage8<int>) << std::endl;
Storage8<bool> boolStorage;
for (int count = 0; count < 8; ++count)
boolStorage.set(count, count);
for (int count = 0; count < 8; ++count)
std::cout << boolStorage.get(count) << std::endl;
cout << "Size of Storage8<bool> " << sizeof(Storage8<bool>) << std::endl;
return 0;
}
Storage8.h
#pragma once
template <class T>
class Storage8
{
private:
T m_array[8];
public:
void set(int index, const T& value)
{
m_array[index] = value;
}
const T& get(int index)
{
return m_array[index];
}
};
template<>
class Storage8<bool>
{
private:
unsigned char m_data;
public:
Storage8() : m_data(0)
{}
void set(int index, bool value)
{
unsigned char mask = 1 << index;
if (value)
m_data |= mask;
else m_data &= ~mask;
}
bool get(int index)
{
unsigned char mask = 1 << index;
return (m_data & mask) != 0;
}
};
13.6 템플릿을 부분적으로 특수화하기
#include <iostream>
#include <array>
#include "Storage8.h"
using namespace std;
template <class T, int size>
class StaticArray_BASE
{
private:
T m_array[size];
public:
T* getArray() { return m_array; }
T& operator[](int index)
{
return m_array[index];
}
void print()
{
for (int count = 0; count < size; ++count)
cout << (*this)[count] << ' ';
cout << endl;
}
};
template<class T, int size>
class StaticArray : public StaticArray_BASE<T, size>
{
};
template<int size>
class StaticArray<char, size> : public StaticArray_BASE<char, size>
{
public:
void print()
{
for (int count = 0; count < size; ++count)
cout << (*this)[count];
cout << endl;
}
};
int main()
{
StaticArray<int, 4> int4;
int4[0] = 1;
int4[1] = 2;
int4[2] = 3;
int4[3] = 4;
int4.print();
StaticArray<char, 14> char14;
strcpy_s(char14.getArray(), 14, "Hello World!");
char14.print();
return 0;
}
함수 타입에 대해 상속을 이용해 specialization 실행.
13.7 포인터에 대한 템플릿 특수화
#include <iostream>
#include <array>
using namespace std;
template<class T>
class A
{
private:
T m_value;
public:
A(const T& input)
: m_value(input)
{
}
void print()
{
cout << m_value << endl;
}
};
int main()
{
A<int> a_int(123);
a_int.print();
int temp = 456;
A<int*> a_int_ptr(&temp);
a_int_ptr.print();
double temp_d = 3.141592;
A<double*> a_double_ptr(&temp_d);
a_double_ptr.print();
return 0;
}
포인터에 대한 값은 주소가 나온다.
#include <iostream>
#include <array>
using namespace std;
template<class T>
class A
{
private:
T m_value;
public:
A(const T& input)
: m_value(input)
{
}
void print()
{
cout << m_value << endl;
}
};
template<class T>
class A<T*>
{
private:
T* m_value;
public:
A (T* input)
: m_value(input)
{
}
void print()
{
cout << *m_value << endl;
}
};
int main()
{
A<int> a_int(123);
a_int.print();
int temp = 456;
A<int*> a_int_ptr(&temp);
a_int_ptr.print();
double temp_d = 3.141592;
A<double*> a_double_ptr(&temp_d);
a_double_ptr.print();
return 0;
}
13.8 멤버 함수를 한 번 더 템플릿화 하기 Templatize
#include <iostream>
#include <array>
using namespace std;
template<class T>
class A
{
private:
T m_value;
public:
A(const T& input)
: m_value(input)
{
}
template<typename TT>
void doSomething(const TT& input)
{
cout << typeid(T).name() << " " << typeid(TT).name() << endl;
cout << (TT)m_value << endl;
}
void print()
{
cout << m_value << endl;
}
};
int main()
{
A<int> a(123);
a.print();
a.doSomething(123.4);
a.doSomething<float>(24.3);
return 0;
}
void doSomething()
{
cout << typeid(T).name() << " " << typeid(TT).name() << endl;
cout << (TT)m_value << endl;
}
void print()
{
cout << m_value << endl;
}
};
int main()
{
A<char> a_char('A');
a_char.print();
a_char.doSomething<int>();
return 0;
}
template<typename TT>
void doSomething(const TT& input)
{
cout << typeid(T).name() << " " << typeid(TT).name() << endl;
cout << (TT)m_value << endl;
}
void print()
{
cout << m_value << endl;
}
};
int main()
{
A<char> a_char('A');
a_char.print();
a_char.doSomething(int());
return 0;
}
위의 경우도 가능!!
'개발 공부 > C++' 카테고리의 다른 글
따라하며 배우는 C++ 15. 의미론적 이동과 스마트 포인터 (0) | 2020.07.03 |
---|---|
따라하며 배우는 C++ 14. 예외 처리 (0) | 2020.07.02 |
따라하며 배우는 C++ 12. 가상 함수들 (0) | 2020.07.02 |
따라하며 배우는 C++ 11. 상속 (0) | 2020.07.02 |
따라하며 배우는 C++ 10. 객체 사이의 관계들에 대해 (0) | 2020.07.02 |