후... 연말, 연초에는 안바쁠거라고 하던 회사는 거짓말쟁이다. 야근밖에 안했다.. ㅠㅠ
CRTP(Curiously Recurring Template Pattern)
- 기반 클래스에서 파생 클래스의 이름을 사용할 수 있게 하는 기법
- 파생 클래스를 만들 때 기반 클래스의 템플릿 인자로 파생 클래스 이름을 전달.
이름부터 이상한 반복 템플릿 패턴이다. 어떻게 사용되는지 3개의 예시로 보자
Counter
template<typename T>
struct counter
{
static int objects_created;
static int objects_alive;
counter()
{
++objects_created;
++objects_alive;
}
protected:
~counter()
{
--objects_alive;
}
};
template <typename T> int counter<T>::objects_created(0);
template <typename T> int counter<T>::objects_alive(0);
class X : counter<X>
{
};
// 이렇게 만들면 X에 관한 objects_ 변수들을 접근할 수 있게 된다.
class Y : counter<Y>
{
};
int main()
{
return 0;
}
크게 counter 클래스에 static으로 create, alive 변수를 만들어준다. 그리고 X, Y 클래스 각각 counter의 템플릿 변수로 만들어주면 X를 만들때마다 X의 create, alive의 값이 올라가고 소멸할때 마다 alive의 값이 내려가 이 클래스를 만든 갯수를 파악할 수 있게 된다.
Static Ploymophisom
// StaticPolymophism을 사용하면 가상 함수(Virtual Function)을 구현 가능
template <class Derived>
struct Base
{
void interface()
{
static_cast<Derived*>(this)->implementation();
}
static void static_func()
{
Derived::static_sub_func();
}
};
struct Derived : Base<Derived>
{
void implementation();
static void static_sub_func();
};
int main()
{
return 0;
}
이 방법은 가상함수를 만들지 않아도 가상함수처럼 작동하는 함수를 만들 수 있다.
interface를 먼저 호출하게 되는데 그렇게 되면 template 변수인 Derived의 implementation을 호출하게 된다.
static 멤버 함수도 마찬가지다.
Rebind
std::vector<int> a;
각 노드는 integer크기 만큼의 메모리를 사용하게 된다.
std::set<int> b;
set 은 RB 트리이기 때문에 integer와 양쪽 노드의 크기만큼의 메모리를 사용하게 된다.
그렇게 되면 set<int>의 allocator는 int가 아닌 int와 양쪽 노드를 모두 allocation을 해야 한다.
이를 위해 Rebind를 사용하게 된다.
// Custom Allocator가 유저가 정의한 타입이 아닌 타입의 추가한 정보를 allocation을 해야 할 때가 있음.
// set<int> 처럼 RB Tree를 사용할때 int가 아닌 left right 포인터를 allocation을 해야 함.
#include <iostream>
#include <iterator>
template<typename T>
class KType
{
public:
template <typename U>
struct rebind
{
typedef KType<U> other; // 자기 자신을 참조 이렇게 되면 U에 대해서 같은 KType을 접근 가능해짐
};
KType(){}
KType(const KType&){}
template <typename U>
KType(const KType<U>&){}
int size() { return sizeof(T); }
};
template<typename T, typename TALLOC=KType<T>> // TALLOC은 T를 allocation을 하고있기 때문에 나머지 값(next)을 모름
class KTest
{
public:
typedef T value_type;
struct _Node
{
value_type _data1;
value_type _data2;
_Node* _next;
};
KTest(){}
void PrintNodeSize()
{
TALLOC a;
TALLOC::template rebind<_Node>::template other allocator(a); // 이렇게 하면 T를 allocation을 하는게 아닌 _Node를 allocation을 하게됨
std::cout << allocator.size() << std::endl;
}
};
int main()
{
KTest<int> t;
t.PrintNodeSize();
return 0;
}
결과
12
총 12(int의 크기, pointer의 크기 * 2)를 출력한다.
'C++' 카테고리의 다른 글
9. STL Equality and Equivalence (0) | 2023.02.08 |
---|---|
8. STL Custom Allocator (0) | 2023.02.07 |
6. STL Placement new (0) | 2022.12.17 |
5. Operator Overloading03 operator new (2) | 2022.12.17 |
4. Operator Overloading Ostream (0) | 2022.12.14 |