반응형

Concept의 개념

  • 일반적으로 함수 템플릿(또는 클래스 템플릿)은 모든 타입이 아니라 특정 조건을 만족하는 타입에 대해서 동작하게 됨
  • 타입이 가져야 하는 조건을 코드로 표현 한 것
  • 2000년 중반부터 이야기 되었으나 아직 표준화 되지 않음
  • C++20 표준에 추가될 예정
  • Concept 문법이 도입될 경우 템플릿 코드에 많은 변화가 있게됨
#include <iostream>
using namespace std;

struct Point
{
    int x, y;
};

// 실제 파라미터가 모든 타입이 아닌 연산자 < 가능한 타입에 대해서만 지원하는 템플릿임
template<typename T> T Min(T x, T y)
{
    return y < x ? y : x;
}

int main()
{
    Point p1, p2;
    Min(p1, p2);
}
#include <iostream>
using namespace std;

struct Point
{
    int x, y;
    bool operator<(const Point& p) { return true; }
};

// 1. concept 만들기
template<typename T>
concept bool LessThanComparable = requires(T a, T b)
{
    { a < b }-- > bool;
};

// 2. template 만들때 concept 표기
template<typename T> requires LessThanComparable<T>
T Min(T x, T y)
{
    return y < x ? y : x;
}

// 3. template 만들때 T대신 concept 사용
LessThanComparable Min(LessThanComparable x, LessThanComparable y)
{
    return y < x ? y : x;
}

int main()
{
    Point p1, p2;
    Min(p1, p2);
}
반응형

반응형

Typelist

  • 값이 아닌 타입의 리스트를 보관하는 데이터 타입

  • 템플릿 인자가 1개라도 Typelist를 활용하면 복수의 타입을 만들 수 있음

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, Nulltype>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, Nulltype>>>
#define TYPELIST_2(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, Nulltype>>>>

template<typename T> class xtuple {};

int main()
{
    xtuple<int> t1;

    Typelist<int, NullType> t1;
    Typelist<int, Typelist<int, NullType>> t2;
    Typelist<int, Typelist<int, Typelist<char, NullType>>> t3;
}

 

Typelist Length

  • 타입리스트의 요소 수 구하기

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// 1. 사용하는 모습을 보고 메인 템플릿 생성
template<typename T> struct Length;

// 2. 갯수를 구할 수 있도록 부분 특수화
template<typename T, typename U> struct Length<Typelist<T, U>>
{
    enum { value = Length<U>::value + 1 };
};

// 3. 재귀를 종료하기 위한 특수화
template<> struct Length<NullType>
{
    enum { value = 0 };
};

template<typename T> void test()
{
    cout << Length<T>::value << endl; // 4
}

int main()
{
    test<TYPELIST_4(int, int, double, float)>();
}

TypeAt

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>


// Typelist의 N번째 요소의 타입 구하기
// 메인 템플릿
template<typename T, int N> struct TypeAt
{
    //typedef ? type;
};

// 원하는 타입을 구할 수 있도록 부분 특수화
// N == 0
template<typename T, typename U> struct TypeAt<Typelist<T, U>, 0>
{
    typedef T type;
};

// 원하는 타입을 구할 수 있도록 부분 특수화
// N != 0
template<typename T, typename U, int N> struct TypeAt<Typelist<T, U>, N>
{
    typedef typename TypeAt<U, N-1>::type type;
};

template<typename T> void test()
{
    typename TypeAt<T, 0>::type i; // int
    cout << typeid(i).name() << endl;

    typename TypeAt<T, 1>::type c; // char
    cout << typeid(c).name() << endl;

    typename TypeAt<T, 2>::type d; // double
    cout << typeid(d).name() << endl;

    typename TypeAt<T, 3>::type l; // long
    cout << typeid(l).name() << endl;
}

int main()
{
    test<TYPELIST_4(int, char, double, long)>();
}

 

Append

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// Typelist 끝에 타입 추가하기
template<typename TL, typename T> struct Append;

// TL T
// 1. NullType, NullType => NullType
template<> struct Append<NullType, NullType>
{
    typedef NullType type;
};

// 2. NullType, 임의의 타입 => Typelist<임의의 타입, NullType>
template<typename T> struct Append<NullType, T>
{
    typedef Typelist<T, NullType> type;
};

// 3. NullType, Typelist<Head, Tail> = Typelist<Head, Tail>
template<typename Head, typename Tail> struct Append<NullType, Typelist<Head, Tail>>
{
    typedef Typelist<Head, Tail> type;
};

// 4. Typelist<Head, Tail>, NullType => Typelist<Head, Tail>
template<typename Head, typename Tail> struct Append<Typelist<Head, Tail>, NullType>
{
    typedef Typelist<Head, Tail> type;
};

// 5. Typelist<Head, Tail>, 임의의 타입 => Typelist<Head, Append<Tail, T>::type>
template<typename Head, typename Tail, typename T> struct Append<Typelist<Head, Tail>, T>
{
    typedef Typelist<Head, typename Append<Tail, T>::type> type;
};

template<typename T> void test()
{
    typename Append<T, int>::type t1;
    cout << typeid(t1).name() << endl; // int, char, double, int, NullType
}

int main()
{
    test<TYPELIST_3(int, char, double)>();
}

 

Typelist 예제

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// Holder : 임의 타입의 data 1개 보관
template<typename T> struct Holder
{
    T value;
};

// GenScatterHierachy => MakeCode
template<typename T, template<typename> class Unit> 
class MakeCode : public Unit<T>
{
};

template<template<typename> class Unit> 
class MakeCode<NullType, Unit>
{
};

template<typename Head,             //            , Holder<double>,     empty 
    typename Tail,                  // Holder<int>, MakeCode<double, Unit>, MakeCode<NullType>
    template<typename> class Unit>  // MakeCode<int, Unit>, MakeCode<Typelist<double, NullType>, Unit
    
class MakeCode<Typelist<Head, Tail>, Unit> : public MakeCode<Head, Unit>, public MakeCode<Tail, Unit>
{
    // int value;
    // double value;
};

int main()
{
    // MakeCode의 1번째 인자가 Typelist일때
    MakeCode<TYPELIST_2(int, double), Holder> mc1;  // 기반 클래스 Holder<int>
                                // Holder<int>와 메모리 모양이 동일
    MakeCode<double, Holder> mc2; // Holder<double>
    MakeCode<NullType, Holder> mc3; // Empty
}
반응형

반응형

Member Detect IDioms

  • 클래스의 멤버 타입 존재 여부 확인

  • 클래스의 멤버 함수 존재 여부 확인

//컴파일 타임의 함수 시그니처에 대한 특성만 활용하는 특성을 이용하는 기법

// 함수 시그니처만 사용함으로 구현부가 없어도 괜찮음
char foo(int a);
short foo(double d);

int main()
{
    int n = 10;

    cout << sizeof(n) << endl; // 4
    //cout << sizeof(foo) << endl; // Error
    cout << sizeof(foo(3)) << endl; // 1
    cout << sizeof(foo(3.3)) << endl; // 2
}
#include <iostream>
#include <vector>
using namespace std;

// 멤버 타입을 가진 데이터용 템플릿(반환값이 char로 1바이트)
template<typename T> 
char check(typename T::value_type* a);

// 멤버 타입이 없는 데이터용 템플릿(반환값이 short로 2바이트)
template<typename T>
short check(...);

// 멤버 타입이 없는 데이터
struct NoValueType
{
};
// 멤버 타입이 있는 데이터
struct HasValueType
{
    typedef int value_type;
};

// 멤버 타입 존재를 여부 확인 함수
template<typename T>
struct has_value_type
{
    // 반환값이 char(1바이트)일때는 true, short(2바이트)일때는 false
    static constexpr bool value = (sizeof(check<T>(0)) == 1); 
};

int main()
{
    cout << has_value_type<HasValueType>::value << endl;
    cout << has_value_type<NoValueType>::value << endl;

    HasValueType t1;
    cout << sizeof(check<HasValueType>(0)) << endl;
    NoValueType t2;
    cout << sizeof(check<NoValueType>(0)) << endl;
}
#include <iostream>
#include <vector>
using namespace std;

// 멤버함수가 존재할 경우 
template<typename T> 
char check(decltype(T().resize(0))* a);

// 멤버함수가 존재하지 않을 경우
template<typename T>
short check(...);

// 멤버함수 존재 체크
template<typename T>
struct has_resize
{
    static constexpr bool value = (sizeof(check<T>(0)) == 1); 
};

int main()
{
    // vector는 resize가 있으므로 true 리턴
    cout << has_resize<vector<int>>::value << endl;
    
    // array는 resize가 없으므로 false 리턴
    cout << has_resize<array<int, 10>>::value << endl;
}
반응형

반응형

함수 찾는 순서

  • 1순위 정확한 매칭(exactly matching) 
  • 2순위 템플릿(template)
  • 3순위 가변 인자(variable argument)
#include <iostream>
using namespace std;

template<typename T>
// 2순위 템플릿(template)
void foo(T t) { cout << "T" << endl; }
// 1순위 정확한 매칭(exactly matching)
void foo(int n) { cout << "int" << endl; }
// 3순위 가변 인자(variable argument)
void foo(...) { cout << "..." << endl; }

int main()
{
    foo(3);
}

 

SFINAE

  • Substitution Failure Is Not An Error

  • 함수 템플릿을 사용시 T의 타입이 결정되고 함수를 생성(Instantiation)하려고 할때 리턴 타입이나 함수 인자등에서 치환에 실패하면 컴파일 에러가 아니라, 함수 후보군에서 제외함

  • 동일한 이름의 다른 함수가 있다면 다른 함수를 사용하게 됨

#include <iostream>
using namespace std;

template<typename T>
typename T::type foo(T t) 
{ 
    cout << "T" << endl; return 0; 
}

void foo(...) 
{ 
    cout << "..." << endl; 
}

int main()
{
    // 가변인자 함수보다 템플릿 함수가 우선순위가 높아서 먼저 참조하게 되지만 리턴 타입에서 
    // int 타입::type은 존재하지 않으므로 만족할 수 없으므로 후보군에서 제외되고 가변인자 버전의 함수가 사용됨
    foo(3); // T
}

 

enable_if

  • c++표준에서 지원하는 도구

  • 1번째 인자가 true일 경우만 type이 정의됨

    • enable_if<true, int>::type  -> int

    • enable_if<true>::type -> void

    • enable_if<false, int>::type -> error

    • enable_if<false>::type -> error

#include <iostream>
using namespace std;

template<bool b, typename T = void> struct enable_if
{
};

template<typename T> struct enable_if<true, T>
{
    typedef T type;
};

int main()
{
    //enable_if<true, int>::type t0; // 타입이 int로 결정
    //enable_if<true>::type t1; // 타입이 void로 결정
    //enable_if<false, int>::type t2; // 타입이 없어서 error
}

 

enable_if 예제

  • 정수일때만 동작하는 함수를 만들고 싶을때

    • static_assert

      • 처리하게 되면 조건을 만족하지 않을 경우 컴파일 에러

    • enable_if

      • SFINAE 특성을 활용한 방법

      • 조건을 만족하지 않으면 함수를 생성하지 않고 다른 후보가 있다면 사용함

      • 확장성 유리

#include <iostream>
using namespace std;

// 정수일때만 함수 코드를 생성하고 싶음
template<typename T>
typename enable_if<is_integral<T>::value, int>::type
foo(T a)
{
    cout << "T" << endl;

    return 0;
}

void foo(...)
{
    cout << "not integer" << endl;
}

int main()
{
    foo(2.3);
}

 

enable_if 위치

  • 함수 리턴 타입

  • 함수 인자 타입 -> 생성자에서 주로 사용

  • 템플릿 인자 -> 함수 자체의 모양이 단순해 보이는 장점 있음

#include <iostream>
using namespace std;

// 함수 리턴 타입 위치
template<typename T>
typename enable_if<is_integral<T>::value, int>::type
foo(T a)
{
    cout << "T" << endl;

    return 0;
}

// 함수 인자 타입 위치(리턴 타입이 없는 생성자에서 주로 사용)
template<typename T>
void foo(T a, 
    typename enable_if<
    is_integral<T>::value>::type* = nullptr)
{
    cout << "T" << endl;
}

// 템플릿 인자(함수 자체의 원형이 최대한 유지됨으로 간결해 보이는 장점)
template<typename T, 
    typename enable_if<
    is_integral<T>::value>::type* = nullptr>
void foo(T a)
{
    cout << "T" << endl;
}

// 모두 만족하지 않을때 호출
void foo(...)
{
    cout << "not integer" << endl;
}

int main()
{
    foo(2); // 템플릿 함수 호출(현재 예제코드는 3개의 템플릿이 만족함으로 에러 발생함)
    foo(2.3); // foo(...) 호출
}

 

타입의 종류에 따라 다르게 동작하는 함수 생성 방법

  • type_traits(is_pointer) + if constexpr // C++17에서만 가능
  • type_traits + 함수 오버로딩(false_type, true_type)
    • 디스패치 함수 1개 더 필요
  • type_traits + enable_if
// C++17이상 가능 if constexpr 사용 방법

#include <iostream>
using namespace std;

template<typename T> void printv(const T& v)
{
    if constexpr(is_pointer<T>::value)
        cout << v << ":" << *v << endl;
    else
        cout << v << endl;
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
// type_traits + 함수 오버로딩(false_type, true_type)

#include <iostream>
using namespace std;

template<typename T>
void printv_imp(const T& v, true_type)
{
    cout << v << ":" << *v << endl;
}

template<typename T>
void printv_imp(const T& v, false_type)
{
    cout << v << ":" << endl;
}

template<typename T> void printv(const T& v)
{
    printv_imp(v, is_pointer<T>());
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
// type_traits + enable_if 사용 방법

#include <iostream>
using namespace std;

template<typename T> 
typename enable_if<is_pointer<T>::value>::type 
printv(const T& v)
{
    cout << v << ":" << *v << endl;
}

template<typename T> 
typename enable_if<!is_pointer<T>::value>::type
printv(const T& v)
{
    cout << v << ":" << endl;
}

int main()
{
    int n = 0;
    printv(n);
    printv(&n);
}
반응형

반응형

Policy Based Design

  • 클래스가 사용하는 정책을 템플릿 인자를 통해서 교체 할 수 있게 만드는 디자인

  • 성능 저하 없이 정책을 교체 할 수 있음

  • 대부분의 정책은 담은 "단위 전략 클래스"는 지켜야 하는 규칙이 있음

    • 규칙을 표현하는 코딩 방식은 없음(인터페이스 사용시 가상 함수이므로 약간의 오버헤드 발생), C++20 concept 문법

    • 우리가 만든 동기화 정책클래스는 "lock/unlock" 함수가 필요함

    • 템플릿 기반 라이브러리, 특히 STL에서 널리 사용되는 디자인 기법

#include <iostream>
using namespace std;

// 1개의 클래스로 정책 템플릿 인자를 통해서 다르게 구현 할 수 있도록 디자인
template<typename T, typename ThreadModel> class List
{
    ThreadModel tm;
public:
    void push_front(const T& a)
    {
        tm.lock();
        // 구현부 코드
        tm.unlock();
    }
};

// 싱글 쓰레드 기반 환경용
class NoLock
{
public:
    inline void Lock() {};
    inline void Unlock() {}
};

// 멀티 쓰레드 기반 환경용
class MutexLock
{
public:
    inline void Lock() {};
    inline void Unlock() {}
};

// 환경에 따라서 클래스 생성 시 전략을 선택 할 수 있음
// 싱글 쓰레드용 생성
//List<int, NoLock> s;
// 멀티 쓰레드용 생성
List<int, MutexLock> s;

int main()
{
    s.push_front(10);
}

 

STL allocator

  • C++에서 메모리 할당 방법은 다양함

    • new, malloc, calloc, win32 api, linux system call

  • STL에서는 각 데이터 컨테이너의 템플릿 인자로 메모리 할당 방식에 대한 전략을 선택할 수 있도록 지원함

#include <iostream>
using namespace std;

// STL의 Vector 생각
// 메모리 할당기
template<typename T> class allocator
{
public:
    T* allocate() {}
    void deallocate() {}
};

template<typename T, typename Ax = allocator<T>> class vector
{
    T* buff;
    Ax ax;
public:
    void resize(int n)
    {
        // 버퍼 재할당이 필요하다면 어떻게 할까요?
        // new, malloc, calloc, win32 api, linux system call
        T* p = ax.allocate(n);
        ax.deallocate(p);
    }
};

int main()
{
    vector<int, MyAlloc<int>> v(10);
    v.resize(20);
}

 

rebind

#include <iostream>
using namespace std;

template<typename T> class allocator
{
public:
    T* allocate(int sz) { return new T[sz]; }
    void deallocate(T* p) { delete[] p; }

    template<typename U> struct rebind
    {
        typename allocator<U> other;
    };
};

template<typename T, typename Ax = allocator<T> > class list
{
    struct NODE
    {
        T data;
        NODE* next, * prev;
    };
    //Ax ax; // allocator<int>
    //allocator<int>::rebind<NODE>::other ax; // allocator<NODE> ax;
    typename Ax::template rebind<NODE>::other ax; // allocator<NODE> ax;
public:
    void push_front(const T& a)
    {
        ax.allocate(1);
    }
};

int main()
{
    list<int> s;
    s.push_front(10);
}
반응형

반응형

CRTP 활용한 싱글톤(Singleton) 만들기

  • 싱글톤: 하나의 객체만 생성 할 수 있게 만드는 디자인 패턴
    • private 생성자
    • 복사와 대입 금지
    • 하나의 객체를 만들어서 리턴하는 static 멤버 함수

단일 Singletone 패턴 클래스

#include <iostream>
using namespace std;

class Cursor
{
private:
    Cursor() {}
public:
    Cursor(const Cursor& c) = delete;
    void operator=(const Cursor& c) = delete;

    static Cursor& getInstance()
    {
        static Cursor instance;
        return instance;
    }

};

int main()
{
    Cursor& c1 = Cursor::getInstance();
    Cursor& c2 = Cursor::getInstance();
}

CRTP 패턴 Singleton 패턴 클래스

#include <iostream>
using namespace std;

template<typename T>
class Singleton
{
protected:
    Singleton() {}
public:
    Singleton(const Singleton& c) = delete;
    void operator=(const Singleton& c) = delete;

    static T& getInstance()
    {
        static T instance;
        return instance;
    }
};

class Mouse : public Singleton<Mouse>
{
};

int main()
{
    Mouse& m1 = Mouse::getInstance();
    Mouse& m2 = Mouse::getInstance();
}

 

CRTP 활용한 Unique한 기반 클래스 만들기

  • 기반 클래스의 static memeber data는 모든 파생 클래스에 의해 공유됨

  • 파생 클래스 별로 다른 static member data가 필요한 경우, 서로 다른 기반 클래스를 사용해야 함

  • CRTP를 사용하면 모든 파생 클래스 별로 다른 타입의 기반 클래스를 만들 수 있음

static 멤버 데이터를 관리하는 단일 클래스

#include <iostream>
using namespace std;

class Object
{
public:
    static int cnt;

    Object() { ++cnt; }
    ~Object() { --cnt; }

    static int getCount() { return cnt; }
};
int Object::cnt = 0;

int main()
{
    Object c1, c2;
    cout << c1.getCount() << endl;
}

static 멤버 데이터를 관리하는 유일한 기반 클래스

#include <iostream>
using namespace std;

template<typename T>
class Object
{
public:
    static int cnt;

    Object() { ++cnt; }
    ~Object() { --cnt; }

    static int getCount() { return cnt; }
};
template<typename T> int Object<T>::cnt = 0;

class Mouse : public Object<Mouse>
{

};

class Keyboard : public Object<Keyboard>
{

};

int main()
{
    Mouse m1, m2;
    Keyboard k1, k2;
    cout << m1.getCount() << endl;
    cout << k1.getCount() << endl;
}
반응형

반응형

CRTP(Cruiously Recurring Template Pattern) 활용

  • 기반 클래스에서 파생 클래스의 이름을 사용할 수 있게 하는 기법

  • 파생 클래스를 만들때 기반 클래스의 템플릿 인자로 파생 클래스 이름을 전달

기준 코드

  • 메인 함수 호출 시 기반 클래스의 OnClick 함수가 호출됨
  • 파생 클래스의 OnClick 함수를 호출하기 위해서는?
#include <iostream>
using namespace std;

class Window
{
public:
    void msgLoop()
    {
        OnClick();
    }
    void OnClick() { cout << "Window OnClick" << endl; }
};

class FrameWindow : public Window
{
public:
    void OnClick() { cout << "FrameWindow OnClick" << endl; }
};

int main()
{
    FrameWindow fw;
    fw.msgLoop(); // OUTPUT : Window OnClick
}

 

구현 코드

  • 비 가상 함수(Non virtual function)를 가상 함수 처럼 동작하게 함
  • 파생 클래스를 만들때 기반 클래스의 템플릿 인자로 파생 클래스 이름을 전달
    • this 포인터를 파생 클래스 타입으로 캐스팅 후 함 수 호출
#include <iostream>
using namespace std;

template<typename T>
class Window
{
public:
    void msgLoop()
    {
        // this 포인터를 파생 클래스인 T 타입으로 캐스팅 후 호출    
        static_cast<T*>(this)->OnClick();
    }
    //가상함수로 만들면 가상함수 테이블이 관리됨
    void OnClick() { cout << "Window OnClick" << endl; }
};

class FrameWindow : public Window<FrameWindow>
{
public:
    void OnClick() { cout << "FrameWindow OnClick" << endl; }
};

int main()
{
    FrameWindow fw;
    fw.msgLoop(); // OUTPUT : FrameWindow OnClick
}
반응형

반응형

thin template

  • 템플릿의 단점 중 한가지는 코드 메모리의 증가
  • 모바일등 메모리가 부족한 환경에서 사용할 수 있는 기법

기준 코드

  • 4개의 함수 구성 * 3개의 타입 사용 = 총 12개의 함수 생성

#include <iostream>
using namespace std;

template<typename T> class Vector
{
    T* buff;
    int sz;
public:
    int size() const {}
    bool empty() const {}
    void push_front(const T& a) {}
    T& front() {}
};

int main()
{
    Vector<int> v1;
    Vector<short> v2;
    Vector<double> v3;
}

T를 사용하지 않는 모든 멤버를 기반클래스에 정의하고 상속받아서 구현

  • 2개의 함수 구성 * 3개의 타입 사용 + 기반 클래스 함수 2개 = 총 8개의 함수 생성

#include <iostream>
using namespace std;

class VectorBase
{
protected:
    int sz;
public:
    int size() const {}
    bool empty() const {}
};

template<typename T> class Vector : public VectorBase
{
    T* buff;
public:
    void push_front(const T& a) {}
    T& front() {}
};

int main()
{
    Vector<int> v1;
    Vector<short> v2;
    Vector<double> v3;
}

모든 멤버를 기반 클래스로 구현

  • 모든 T는 void*로 변경

  • void*를 바로 사용하면 캐스팅이 불편함으로 캐스팅을 책임지는 파생 클래스를 템플릿으로 제공

#include <iostream>
using namespace std;

class VectorBase
{
protected:
    void* buff;
    int sz;
public:
    int size() const {}
    bool empty() const {}
    void push_front(const void* a) {}
    void* front() {}
};

// void*를 바로 사용하면 캐스팅의 불편함이 있음
// 캐스팅을 책임지는 파생 클래스를 템플릿으로 제공
// 캐스팅만 책임지는 함수는 inline로 치환하면 함수화 되지 않음
template<typename T> class Vector : public VectorBase
{
public:
    inline void push_front(const T& a) { VectorBase::push_front(static_cast<void*>(a)) }
    inline T& front() { return static_cast<T&>(VectorBase::front()); }
};

int main()
{
    Vector<int> v1;
    Vector<short> v2;
    Vector<double> v3;
}
  • 기반 클래스 멤버를 직접 호출 방지를 위해 private 상속으로 변경

#include <iostream>
using namespace std;

class VectorBase
{
protected:
    void* buff;
    int sz;
public:
    int size() const {}
    bool empty() const {}
    void push_front(const void* a) {}
    void* front() {}
};

// void*를 바로 사용하면 캐스팅의 불편함이 있음
// 캐스팅을 책임지는 파생 클래스를 템플릿으로 제공
template<typename T> class Vector : private VectorBase
{
public:
    int size() const { return VectorBase::size(); }
    bool empty() const { return VectorBase::empty(); }
    inline void push_front(const T& a) { VectorBase::push_front(static_cast<void*>(a)); }
    inline T& front() { return static_cast<T&>(VectorBase::front()); }
};

int main()
{
    Vector<int> v1;
    Vector<short> v2;
    Vector<double> v3;
}
반응형

+ Recent posts