템플릿★

2020. 11. 19. 22:47컴퓨터 수업/C++

템플릿 함수

기본

template <typename T>
T get(T _t){
    return _t;
}

오버로딩 

template <typename T>
void print(T _t, int x){
    cout <<'['<< _t <<','<<x<<']' << endl;
}

명시적 특수화

template <typename T>
void print(T _t){
    cout << _t << endl;
}
template <>
void print<int>(int _t){
    cout << _t << endl;
}

오버로딩과 명시적 특수화를 보면,

 

오버로딩은 저 statment만으로 형식을 만들어내지만,

명시적 특수화는 원래 statment가 필요하다.

 

☆사용방안의 차이는,

1. 예를들어, 입력되는 값이 무엇이든지 간에, int로 묵시적 형변환을 수행하고 싶다 -> 오버로딩

2. 예를들어, 입력되는 값이 int일때는 특별한 행동을 하고, "나머지 형"에는 정상 작동을 하고 싶다-> 명시적 특수화


반환형 추론

방법1 (C++ 11)

template<typename T, typename U>
auto sum(T _t, U _u)->decltype(_t+_u){
    return _t+_u;
}
int main(){
    cout << sum(1,2.5) << endl;
}

방법2 (C++ 14)

template<typename T, typename U>
decltype(auto) sum(T _t, U _u){
    return _t+_u;
}
int main(){
    cout << sum(1,2.5) << endl;
}

개인적으로 C++11의 방식이 좀 더 후행 반환 형식인것 같아서 조음!


템플릿 클래스 (NOT 클래스 템플릿)

기본

template <typename T>
class Myclass{
private:
    T data;
};

오버로딩(은 아니고 수식 매개변수)

template <typename T,int N>
class Myclass{
private:
    T data[N];
};
int main(){
    Myclass<int,3> ob1;
}

부분 특수화

template <typename T, typename U>
class Myclass{
private:
    T data1;
    U data2;
};

template <typename T>
class Myclass<T,int>{
private:
    T data1;
    int data2;
};
int main(){
    Myclass<int,int> ob1;
}

함수템플릿의 명시적 특수화에서 처럼

1. 원시 템플릿을 필요로하며,

2. 명시적으로 특수화 할 typename은 지운다.

(template<typename T>가 template<> 가 된 것 처럼.)

3. 함수 템플릿때와 달리 Myclass<T,int> 라고 명시적으로 표기해야함.

명시적 특수화

template <typename T, typename U>
class Myclass{
private:
    T data1;
    U data2;
};

template <>
class Myclass<int,int>{
private:
    int data1;
    int data2;
};

부분 특수화 이해했으면 굳이...


typename 얽힘 현상.

얽힘1.

template<typename T, typename U>
void sum(T t, U u){
    return t+u;
}

만약, typename이 array의 size같이 4개 (string, char, int , double) 이 준비되어 있다면,

위의 함수는 총 4 x 4 = 16 의 경우의수로 instantiation 될 수 있다.

얽힘2.

template<typename T>
class Myclass{
public:
    template<typename TT>
    void SomeFunction (){}
};
int main(){
    return 0;
}

이도 마찬가지로 4 x 4 = 16 의 얽힘이 발생하게 된다. 다음 예제와 얽힘2를 비교해보자.

안얽힘.

template<typename T>
class Myclass{
public:
    void SomeFunction (){}
};
int main(){
    return 0;
}

근본적으로 클래스 템플릿 내부에선 생략되는것이 2개 있다.

1. Myclass<T>가 늘 생략된다. 클래스 내부에서 자신과 같은 형을 호출할때는 묵시적으로 <T>가 붙는다.

2. 메서드의 type binding이 T로 동적 바인딩 되어있다.


typename 얽힘 현상 심화 (포기)

원래 다음 stackoverflow의

이 부분을 설명하고자 하였으나. 특수화 되지 않은 클래스의 내의 템플릿 함수를 특수화 할 수 없어서 다음과 같은 해괴한 코드가 되었다... (컴파일은 됀다.)

template<typename T>
class Myclass{
public:
    template<class TT>
    void SomeFunction ();
};

template<typename T>
template<class TT>
void Myclass<T>::SomeFunction(){
    cout << "Default" << endl;
}
template<>
template<>
void Myclass<double>::SomeFunction<int>(){
    cout << "Int" << endl;
}

template<>
template<>
void Myclass<double>::SomeFunction<double>(){
    cout << "Double" << endl;
}

int main(){
    return 0;
}

프렌드 함수

템플릿 클래스와 비템플릿 함수의 얽힘.

template <typename T>
class MyClass{
private:
    int x;
public:
    MyClass():x(0){}

    friend void Show(const MyClass<T> &);
};
void Show(const MyClass<int> &ob){
    cout << ob.x << endl;
}

int main(){

    MyClass<int> ob1;
    Show(ob1);
}

당연히 제네릭하게 함수를 사용하려고 했는데, 지금 MyClass를 double로 호출해버리면,

Show 메서드를 호출하지 못하므로 warning이 뜬다.

템플릿 클래스와 템플릿 함수의 얽힘

1. 바운드 - 템플릿 클래스가 T 자료형이면 템플릿 함수도 T 고정이다.

<introvert>

template <typename T>
class MyClass{
private:
    int x;
public:
    MyClass():x(7){}

    friend void Show(const MyClass<T> & ob){
        cout << ob.x << endl;
    }
};

int main(){
    MyClass<int> ob1;
    Show(ob1);
}

프렌드 함수를 클래스 함수 내부에 정의할거면...거.... 의미가...있나...?

<extrovert>

//4
template <typename T> 
class MyClass;

//3
template <typename T>
void Show(const MyClass<T>  &);

template <typename T>
class MyClass{
private:
    int x;
public:
    MyClass():x(7){}

    //1
    friend void Show<T>(const MyClass<T> & ob); // <T>가 <>로 바뀌어도 상관 없다.
};

//2
template<typename T>
void Show(const MyClass<T> & ob){
    cout << ob.x << endl;
}

int main(){
    MyClass<int> ob1;
    Show(ob1);
}

보통 1,2,3,4 순으로 필요한것이 맞는데,

이 방식이 위의 (템플릿 클래스와 비템플릿 클래스의 얽힘)과 달리 위에 friend 함수의 위에 전방 선언을 해야하는 이유는, Show<T> 라는 statement로 명시적 특수화를 행하면서 memory binding이 필요하므로 Show라는 함수의 실체가 필요하기 때문이라고 사료된다.

 

★그리고 꼭 집고 넘어가야 할 것이,

바운드형식의 extrovert와 introvert는 사실 같은 형식이 아니다.

왜냐하면, introvert는 그저 T로 동적바인딩 된 Show함수를 정의한것이고.

extrovert 방식은 show라는 함수를 class 밖으로 데리고 나가는 순간부터, 원래 typename인 T를 잃게 되므로, show도 템플릿 클래스로 정의하고, 명시적 특수화를 통해, T에 대한 정의를 작성한것이다. 그림으로 보자면,

2. 언바운드 - 템플릿클래스와 템플릿 함수의 typename이 얽힌다.

즉, 위에서 말한 4 x 4 = 16 과 같은 intance들이 생긴다.

<introvert>

template <typename T>
class MyClass{
private:
    int x;
public:
    MyClass():x(7){}

    template<class C>
    friend void Show(const C & ob){ //<- 이 부분이 중요하다 "어떤" 클래스타입을 받을 지 모른다.
        cout <<ob.x << endl;
    }

};

int main(){
    MyClass<int> ob1;
    Show(ob1); // <- 위와 마찬가지.
}

<extrovert>

template <typename T>
class MyClass{
private:
    int x;
public:
    MyClass():x(7){}

    template<class C>
    friend void Show(const C & ob);
};

template<class C>
void Show(const C& ob){
    cout << ob.x << endl;
}

int main(){
    MyClass<int> ob1;
    Show(ob1); // <- 위와 마찬가지.
}

끝!

'컴퓨터 수업 > C++' 카테고리의 다른 글

#include <algorithm>  (0) 2020.12.01
Lambda Function  (0) 2020.11.30
템플릿(임시)  (0) 2020.11.19
virtual , 업/다운 캐스팅, 정적타입바인딩, 추상클래스 ★  (0) 2020.11.10
C++ 상속  (0) 2020.11.05