Feedback : smile.k06@gmail.com


그냥 코딩하다가 오류나고 생각나서 끄적끄적..

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
32
33
34
35
#include <iostream>
 
template <typename T>
class testClass 
{
    // class 내부함수
public:   
      void testFunc();   
      void testFunc2();
    
    // 생성자, 파괴자
public:
      testClass();
      ~testClass();
};
 
// class 내부함수
void testClass::testFunc() {};   // C2955 에러
            
template <typename T>
void testClass<T>::testFunc2 {};    // 정상 컴파일
 
// 생성자
testClass::testClass();     //C2955 에러
 
template<typename T>
testClass<T>::testClass {};    //정상 컴파일
 
// 파괴자
testClass::~testClass();     //C2955 에러
 
template<typename T>
testClass<T>::~testClass {};    //정상 컴파일
 
 

testClass 를 template 화 시켰다.

비쥬얼 스튜디오에서 클래스 생성기로 클래스를 생성하면 생성자와 파괴자가 cpp파일에 자동 생성.
그러나 헤더파일에서 클래스를 템플릿 시켜주고 컴파일하면 그냥 오류가 남.

이거이가 무슨말이고 한가 해서.. 테스트를 해 보었다.


template 클래스는 컴파일이 됨과 동시에 타입을 생성해낸다. 입력과 동시에 타입생성이 아니라는 것.
외부에 함수를 정의할때 따라서 문제가 생긴다.
외부에 단순히 scope로 함수를 인식하게 해준다면,
컴파일은
"얘 타입 없음"
이라고 오류를 뱉는것임.

따라서 템플릿을 꼭 붙여주도록 해야겠다.

안뇽~





반응형

BLOG main image




특별히 추상클래스를 부모로 둘 필요가 없다 생각되고 각 다른타입으로 생성될 템플릿 클래스들이 자식으로 있을때

공통적으로 업캐스팅을 하고자 할때 다음처럼 사용


comment : 자식은 각 고유의 타입마다 다른 형태로 컴파일 타임에 인스턴스화 되어야 할때 사용

그외에도 목적에 맞게 사용하면 그만이지 뭐..






class objectBase_sjh{

public :

virtual ~objectBase_sjh(){};

virtual HRESULT renderGeoMatricalNormal(){ return S_OK; };                            //템플릿이 아닌 클래스에서의 가상함수

};





template<typename T>                                                                                            //템플릿이 아닌 클래스 objectBase_sjh 를 상속받지만

class objectCommon_sjh : public objectBase_sjh{                                                    //objectCommon_sjh  클래스는 템플릿 클래스

protected:

typedef std::vector<TEXTUREINFO_SJH> vecTextureInfo_sjh;                            

objectCommon_sjh(){}


public :

  //부모에서 정의한 virtual 을 따로 정의해줄 필요는 없다, 필요하다면 재정의


};




template<typename T>

class xFileObject_sjh : public objectCommon_sjh<T>{

public :

T _data;

public :

xFileObject_sjh(){

_data=60;

}

~xFileObject_sjh(){}

explicit xFileObject_sjh( LPDIRECT3DDEVICE9 pD3dDevice,  

std::wstring xFileName=_T(""), 

std::wstring textureName=_T(""), 

const std::wstring& directoryPath=_T("images/")   )

}


public :


virtual HRESULT renderGeoMatricalNormal(){ std::cout<<"xFileObject_sjh\t"<<_data<<std::endl;  return S_OK; }           //xFileObject_sjh 60      가 출력된다


};


//////////////////////////////////////////////////////////////////////////


template<typename T>                                                                                     //objectCommon_sjh 과는 다른 용도로 사용될 동일한 objectBase_sjh 를 상속받는

class objectOther_sjh : public objectBase_sjh{                                                  //템플릿클래스

protected:

typedef std::vector<TEXTUREINFO_SJH> vecTextureInfo_sjh;

objectOther_sjh(){}


public :




};



template<typename T>

class renderObject_sjh : public objectOther_sjh<T>{

public :

T _data;

public :

renderObject_sjh(){

_data=30.5;

}

~renderObject_sjh(){}

explicit renderObject_sjh( LPDIRECT3DDEVICE9 pD3dDevice,  

std::wstring xFileName=_T(""), 

std::wstring textureName=_T(""), 

const std::wstring& directoryPath=_T("images/")   )

}


public :



virtual HRESULT renderGeoMatricalNormal(){ std::cout<<"renderObject_sjh\t"<<_data<<std::endl;  return S_OK; }            //renderObject_sjh 30.5      가 출력된다


};



int main()

{

objectBase_sjh* instance1=new xFileObject_sjh<int>;


instance1->renderGeoMatricalNormal();



objectBase_sjh* instance2=new renderObject_sjh<float>;

instance2->renderGeoMatricalNormal();




getchar();


delete instance2;

delete instance1;


return 0;


}








반응형
BLOG main image


중요한것은 특수화가 되는 두번째 클래스나 세번째 클래스에서의 class a  옆에 오는 < , > 이 자리에 typename T1, 또는 T2 가 오는 위치이며 특수화시 
template<typename T1> 나 template<typename T2> 또는  template<typename T3> 으로 해도 상관은 없다
(단!  T3으로 했을때 틈수화 템플릿 내부에서도 T3으로 맞춰야한다 )


template<typename T1, typename T2>

class a{

public :

a(){

_t1=T1();

_t2=T2();

std::cout<<_t1<<"\t"<<_t2<<std::endl;

}

T1 _t1;

T2 _t2;

};



template<typename T1>

class a <T1,int>

{

public :

a(){

_t1=T1();

_t2=300;

std::cout<<_t1<<"\t"<<_t2<<std::endl;

}

T1 _t1;

int _t2;

};


template<typename T3>

class a <char,T3>

{

public :

a(){

_t1='t';

_t2=T3();

std::cout<<_t1<<"\t"<<_t2<<std::endl;

}

char _t1;

T3 _t2;

};


int main() 

{

a<int,float> ai1;

a<int,int> ai2;

a<char,float> ai3;

//a<char,int> ai4; //error : T1 과 T2 자리에 대한 특수화가 모두다 있음으로

return 0;

}


반응형
http://ikpil.com/1021

인라인이 무엇인지 알기(exceptional C++ 에 무척 자세히 나온다: 항목 25 : inline 해부 http://www.ikpil.com/821)기 때문에 생략 한다. 

템플릿의 특성상 번역단위마다 코드 인스턴스화가 일어 나지만, 동일한 인스턴스일 때는 단 1개의 정의로 컴파일러는 관리 한다. 그러므로 대부분의 컴파일러에서 "재정의 오류"가 일어나지 않는다. 일어 나는 컴파일러도 있다고 책에 적혀 있지만, 그건 프로그래머 잘못이 아니다. .. 그러므로 프로그래머는 당장 컴파일러를 바꾸어야 한다.(이건 책에 내용이 없다 : ) )

하여튼, 번역단위마다 코드 인스턴스화가 일어 난다고 해서 자동으로 인라인이 된다고 착각 할 수 있다. 이 착각은 인라인과 동일하게 번역 단위마다 코드 붙이기가 일어 난다고 생각 하기 때문이다.

다시 말하지만 이건 착각이다. 그러므로 함수 템플릿이라 하더라도 빠른 호출을 하기 위해선 inline 키워드를 써 주어야 한다.


반응형
http://saelly.tistory.com/144


- 함수 템플릿과 static지역변수 -


static 지역변수는 템플릿 함수 별로 각각 존재하게 된다.



실행결과로 알 수 있듯이, 컴파일러에 의해서 만들어진 템플릿 함수 별로 static 지역변수가 유지됨을 보이고 있다.






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


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

템플릿 클래스 별로 static 멤버변수를 유지하게 된다.



32~33행: 10과 15를 더하고 있다, 그래서 25가 출력.


36~37행: 26행에서 정의한 static 특수화로 인하여 mem이 5로 초기화되고 거기에 100이 더해져서 105가 출력된다.


언제 template <typename T> 를 쓰고 언제 template <> 를 쓰는가?


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


template <typename T>

class Simple

{

public:

T SimpleFunc(T num) { ... }

};

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




template <>

class Simple<int>

{

public:

int SimpleFunc(int num) { ... }

};

이 정의의 핵심은 <int>이다. 그런데 이 역시 템플릿 관련 정의이기 때문에, 이러한 사실을 알리기 위한 선언이 필요하다. 

하지만 이 정의에서는 T라는 문자가 등장하지 않으니, template <> 을 선언하는 것이다. 


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





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


특수화는 함수 템플릿 또는 클래스 템플릿만을 대상으로 진행할 수 있는 것이 아니다.

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


template <>

long SimpleStaticMember<long>::mem = 5;


반응형


Introduction

클래스 템플릿이 다른 클래스 템플릿을 상속 할 경우, 무엇을 주의 하라는지 설명 하고 있다. 클래스 템플릿을 상속 하고 잘못 사용 했을 경우, "가상 함수"가 제 역활을 못할 수 있거나, 컴파일 자체가 되지 않을 수 있으므로, 이 부분은 꼭 봐야 할 것이다.

Content

시작하기에 앞서, 한가지 사실을 미리 알아두어야 한다. 바로 기반이 되는 클래스 템플릿이 파생 되는 클래스 템플릿에 기입 된 파라미터에 종속 될 경우와 종속되지 않을 경우, 이름을 찾는 규칙이 변화 한다는 사실이다.

이 두 경우 중 종속되지 않을 경우(기반 클래스 템플릿이 파생 클래스 템플릿에 기입된 템플릿 파라미터에)가 더 적은 규칙을 가지고 있으므로, 이를 먼저 정리하겠다.

1. 종속되지 않은 기반 클래스일 경우 주의 해야 할 점

종속되지 않은 기반 클래스일 경우, 일반 클래스를 상속 할 때와 거의 똑같다. 다른 점이 있다면, 한정된 이름이 파생 클래스 템플릿에 기입된 템플릿 파라미터 식별자(이름)와 종속되지 않은 기반 클래스에 있는 이름에 같이 있을 경우, 제일 먼저 종속 되지 않는 기반 클래스에 있는 이름을 먼저 찾으므로, 뜻하지 않게 컴파일이 안 될 수 있다.

template <typename X>
class Base
{
public:
int basefield;
typedef int T; // 이 부분이 중요.
};
template <typename T>
class D2 : public Base<double> // 종속되지 않은 기반 클래스
{
public:
void f()
{
basefield = 7;
}
T strage;
};
void g( D2<int*>& d2, int *p )
{
d2.strage = p; // 이 녀석이 컴파일 되지 않음
}
int main( void )
{
}

여기서 봐야 할 것은 d2.strage이 컴파일 되지 않는 것 이다. 왜냐하면 d2.strage는 int 형이기 때문이다. 아무리 템플릿 파라미터로 int* 를 기입하였어도 int로 기입되었다고 한다. 그 이유가 바로, 식별자 "T"를 평가 할 때, 템플릿 파라미터로 평가 된게 아니라 기반 클래스의 "typedef int T"로 평가 되었기 때문이다.

이는 종속되지 않은 기반 클래스에서 먼저 이름을 찾기 때문인 것을 증명한 셈이다. (이름을 중복되지 않게 짓는 습관을 가진 다면, 이러한 문제는 발생 되지 않을 것이다.)

그렇다면
2. 종속된 기반 클래스일 경우 주의 해야 할 점은 무엇인가?
종속되지 않은 기반 클래스일 경우, 그 기반 클래스 부터 이름을 찾는다고 했는데, 여기선 사용하는 식별자가 한정화 되지 않거나, 종속되지 않으면, 종속된 기반 클래스에서 이름 자체를 찾지 않는다. 이것이 다른 점이다.(하지만 MSVC 에선 찾는다. ... 표준하고 다르다..) 그 이유는 이름을 찾을 수 있게 될 경우, 템플릿 특수화로 이름의 상태가 변경(int형 변수에서 열거형 상수로)될 수 있기 때문이라고 한다.



template <typename X>
class Base
{
public:
int basefield;
};
template <typename T>
class DD : Base<T>
{
public:
void f()
{
basefield = 0;
}
};
template <>
class Base<bool>
{
public:
enum
{
basefield = 42, // basefield의 의미가 변했다.
};
};


이렇게 이름을 찾을 수 없게 함으로써 우회 할 순 있으나, 그 방법이 있는데, this를 사용하여 한정 시키는 방법과 한정자(::)를 사용하여 한정 시키는 방법, 종속시키는 방법, using을 사용 하는 방법들이 있다.

일반적으로 this를 사용 하는 형태가 더 좋다. 왜냐하면 한정자를 사용하는 방법은 자칫 가상함수 호출을 억제 할 수 있기 때문이다. 그러므로 가상 함수 호출에선 this를 사용하고, 선언같은 경우는 한정자를 사용 하면 된다.



template <typename T>
class B
{
public:
enum E
{
e1 = 6, e2 = 28, e3 = 496,
};
virtual void zero( E e = e1 );
virtual void one(E&);
};
template <typename T>
class D : public B<T>
{
public:
void f()
{
// this->E e 라고 선언 할수 없다!
// 이 경우가 바로 한정자 를 사용 해야만 할 때이다.
typename D<T>::E e;
// D<T>::zero() 라고 호출하면 가상 함수를 호출 할 수 없다.
// 그러므로 this를 사용 한다.
this->zero();
// e가 종속적인 데이터이므로 one은 자동적으로 종속적인
// 함수가 되므로, 기반 클래스 템플레서 찾을 수 있다.
one(e);
}
};

3. using을 사용 할 때 주의 해야 할 점

바로 다중 상속을 할 경우, 동일한 이름이 있을 때, 원하는 것을 정확하게 선택하는 것을 프로그래머의 몫으로 돌리기 때문이다. 인간이 한다는건 언제나 실수를 동반 할 수 있다는 것이기에 주의 해야 한다는 것이다.



template <typename T>
class Base
{
public:
int basefield;
};
template <typename T>
class Base2
{
public:
int basefield;
};
template <typename T>
class DD3 : public Base<T>, Base2<T>
{
public:
// 이렇게 using을 사용하여 가시화 시킨다.
using Base<T>::basefield;
//using Base2<T>::basefield;
void f()
{
basefield = 0;
}
};
int main( void )
{
DD3<int> d;
d.f();
}

Digression

가상함수 호출 때문에 꼭 this를 써야 하는건 분명 기억하고 넘어가야 한다. 이 부분은 g++ 에서 컴파일 할 수 있는 상황을 갖은 사람만이 해당 될려나? 안 배워서 나쁠껀 없지만...

2부 부분이 워낙 문법적인 이야기만 나오기 때문에, 지치는 경향이 있다. 하지만 3부와 4부의 내용을 흡수하기 위해선 꼭 필요한 내용이니 잊어먹어도 좋으니 익숙해 지는것을 포기해서는 안된다. 또 훗날 C++ Template Metaprogramming 을 해야 하므로, 너무 쳐지지 않도록 해야 한다.

이것으로 템플릿 이름 9장은 끝났다. 

반응형

+ Recent posts