Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C++] 템플릿과 static 본문

Programming/C++

[Programming/C++] 템플릿과 static

scii 2021. 2. 7. 18:38

함수 템플릿과 static 지역 변수

template <typename T>
void ShowStaticValue(void) {
  static T num = 0;
  num += 1;
  cout << num << " ";
}

함수 템플릿 내에 지역변수 num이 static으로 선언되었다. 그런데 위의 '함수 템플릿'을 기반으로 컴파일러는 다음과 같이 '템플릿 함수'들을 만들어 낸다.

void ShowStaticValue<int> (void) {
  static int num = 0;
  num += 1;
  cout << num << " ";
}

void ShowStaticValue<double> (void) {
  static double num = 0;
  num += 1;
  cout << num << " ";
}

따라서 static 지역변수도 템플릿 함수 별로 각각 존재하게 된다.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


template <typename T>
void ShowStaticValue(void) {
    static T num = 0;
    num += 1;
    cout << num << " ";
}


 int main(const int argc, const char* const argv[]) { 
     ShowStaticValue<int>();
     ShowStaticValue<int>();
     ShowStaticValue<int>();
     cout << endl;
     ShowStaticValue<long>();
     ShowStaticValue<long>();
     ShowStaticValue<long>();
     cout << endl;
     ShowStaticValue<double>();
     ShowStaticValue<double>();
     ShowStaticValue<double>();

     return 0;
}
 
/* 결과

1 2 3
1 2 3
1 2 3

*/

컴파일러에 의해서 만들어진 템플릿 함수 별로 static 지역변수가 유지됨을 알 수 있다.


클래스 템플릿과 static 멤버변수

static 멤버변수는 변수가 선언된 클래스의 객체간 공유가 가능한 변수이다. 

template <typename T>
class SimpleStatic {
  private:
    static T mem;
  ...
};

template <typename T>
T SimpleStatic<T>::mem = 0;
...

따라서 위와 같이 클래스 템플릿이 정의되면 컴파일러에 의해서 다음과 같이 템플릿 클래스들이 생성되어 템플릿 클래스 별로 static 멤버변수를 유지하게 된다.

class SimpleStatic<int> {
  private:
    static int mem;
  ...
};

int SimpleStatic<int>::mem = 0;
...
class SimpleStatic<double> {
  private:
    static double mem;
  ...
};

double SimpleStatic<double>::mem = 0;
...

다음의 예제를 살펴보자.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


template <typename T>
class SimpleStaticMem {
private:
    static T mem;

public:
    void AddMem(int num) {
        mem += num;
    }

    void ShowMem(void) const {
        cout << mem << endl;
    }
};

// static 멤버의 초기화 문장
template <typename T>
T SimpleStaticMem<T>::mem = 0;


 int main(const int argc, const char* const argv[]) { 
     SimpleStaticMem<int> obj1;
     SimpleStaticMem<int> obj2;
     obj1.AddMem(2);
     obj2.AddMem(3);
     obj1.ShowMem();

     SimpleStaticMem<long> obj3;
     SimpleStaticMem<long> obj4;
     obj3.AddMem(100);
     obj4.ShowMem();

     return 0;
}
 
/* 결과

5
100

*/

 


template <typename T>와 template <>

언제 template <typename T> 선언이 필요한가?

언제 template <> 선언이 필요한가?

템플릿 관련 정의에는 template <typename T> 또는 template <>와 같은 선언을 둬서, 템플릿의 일부 또는 전부를 정의하고 있다는 사실을 컴파일러에게 알려야 한다.

다음의 경우 템플릿 정의에 T가 등장하므로 template <typename T>의 선언을 통해, T가 의미하는 바를 알려야 한다.

template <typename T>
class Simple {
  public:
    T Simple(T num) { ... }
};

다음은 클래스 템플릿을 int형에 대해서 특수화 한 결과이다.

template <>
class Simple<int> {
  public:
    int Simple(int num) { ... }
};

이 정의의 핵심은 <int>이다. 그런데 이 역시 템플릿 관련 정의이기 때문에, 이러한 사실을 알리기 위한 선언이 필요하다. 하지만 이 정의에서는 T라는 문자가 등장하지 않으니, template <>을 선언하는 것이다. 

즉, 정의 부분에 T가 존재하면 T에 대한 설명을 위해서 <typename T>의 형태로 덧붙이면 되고, T가 존재하지 않으면 <>의 형태로 간단하게 선언하면 된다.


템플릿 static 멤버변수 초기화의 특수화

다음과 같은 형태로 static멤버를 초기화하였다. 때문에 모든 mem은 0으로 초기화된다.

template <typename T>
T SimpleStaticMem<T>::mem = 0;

그런데 long 타입의 static 멤버변수는 0이 아닌 다른 값으로 초기화하려면 어떻게 해야 할까? 이 때 필요한 것이 멤버변수 초기화의 특수화이다.

초기화문에 대해서 특수화를 진행하면 된다. 특수화는 함수 템플릿 또는 클래스 템플릿만을 대상으로 진행할 수 있는 것이 아니다. 

클래스 템플릿 정의의 일부인 초기화문을 대상으로도 진행이 가능하다. 다양한 특수화와 마찬가지로 T를 대신해서 특수화하고자 하는 자료형의 이름을 삽입하면 된다.

template <>
long SimpleStaticMem<long>::mem = 5;

T를 long으로 바꾼 결과로 T가 모두 사라졌다. 그래서 <typename T>를 대신해서 <>을 사용해 템플릿 정의의 일부임을 알려야 한다.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


template <typename T>
class SimpleStaticMem {
private:
    static T mem;

public:
    void AddMem(int num) {
        mem += num;
    }

    void ShowMem(void) const {
        cout << mem << endl;
    }
};

// static 멤버의 초기화 문장
template <typename T>
T SimpleStaticMem<T>::mem = 0;

// static 멤버변수에 특수화 진행
template <>
long SimpleStaticMem<long>::mem = 5;

 int main(const int argc, const char* const argv[]) { 
     SimpleStaticMem<int> obj1;
     SimpleStaticMem<int> obj2;
     obj1.AddMem(2);
     obj2.AddMem(3);
     obj1.ShowMem();

     SimpleStaticMem<long> obj3;
     SimpleStaticMem<long> obj4;
     obj3.AddMem(100);
     obj4.ShowMem();

     return 0;
}
 
/* 결과

5
105

*/
Comments