ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 항목 1. 템플릿 형식 연역 규칙을 숙지하라.
    프로그래밍/Modern Effective c++ 2016. 3. 13. 23:54

    auto는 템플릿에 대한 형식 연역을 기반으로 작동한다.

    하지만 이것이 auto에 적용될 때 템플릿에 적용될 때보다 덜 직관적인 경우가 있다.

    따라서 제대로 이해하기 위해서는 템플릿 형식 연역을 제대로 이해해야한다.


    템플릿은 일반적으로 다음의 형태를 가진다.

    template<typename T>

    void f(ParamType param);

    f(expr); //호출


    ParamType의 형태를 기준으로 세가지 경우를 살펴보자.

    경우 1. ParamType이 포인터 또는 참조 형식이지만, 보편참조는 아닌 경우.

    1. expr이 참조 형식이면 참조부분을 무시한다. --> 당연하다. expr이 참조형식인것은 자기한테만 중요할뿐 param에게는 아무의미가 없다. 
    2. 그다음 expr의 형식을 ParamType에 대해 패턴 매칭 방식으로 대응시켜서 T의 형식을 결정한다.

    ex1)
    template<typename T>
    void f(T& param);

    int x = 27;
    const int cx = x;
    const int& rx = x

    f(x); --> T : int, param : int&
    f(cx); --> T : const int, param : const int&
    f(rx); --> T : const int, param : const int&

    ex2)

    template<typename T>
    void f(const T& param);

    int x = 27;
    const int cx = x;
    const int& rx = x

    f(x); --> T : int, param : const int&
    f(cx); --> T : int, param : const int&
    f(rx); --> T : int, param : const int&


    ex3)

    template<typename T>
    void f(T* param);

    int x = 27;
    const int* px = &x;

    f(&x); --> T : int, param : int*
    f(px); --> T : const int, param : const int*


    경우 2. ParamType이 보편참조인 경우.


    template<typename T>
    void f(T&& param);

    int x = 27;
    const int cx = x;
    const int& rx = x;

    f(x); --> x: 왼값, T : int&, param : int&
    f(cx); --> x: 왼값, T : int&, param : const int&
    f(rx); --> x: 왼값, T : int&, param : const int&
    f(27); --> x: 오른값, T: int, param : int&&

    이유는 항목 24에서 설명.

    경우 3. ParamType이 포인터도 아니고 참조도 아님.

    인수가 함수에 값으로 전달되는 상황.

    template<typename T>
    void f(T param);

    따라서 param은 주어진 인수의 복사본, 즉 완전히 새로운 객체. 이때문에 expr에서 T가 연역되는 과정에서 다음이 적용된다.

    1. expr이 참조형식이면 참조부분은 무시된다. --> 경우 1과 같이 당연하다.
    2. expr의 참조성을 무시한 후 만일 expr이 const이면 그 const 역시 무시한다.

    template<typename T>
    void f(T param);

    int x = 27;
    const int cx = x;
    const int& rx = x

    f(x); --> T : int, param : int
    f(cx); --> T : int, param : int
    f(rx); --> T : int, param : int

    param은 expr과는 독립적인 새로운 객체이기 때문에 const는 당연히 사라진다.

    다음의 경우는 어떨까.

    template<typename T>
    void f(T param);

    const char* const ptr = "Fuck";

    f(ptr);

    이 경우 param은 ptr을 복사한 새로운 객체이므로 ptr자체의 const성은 사라진다. 
    하지만 ptr이 가리키는 것의 const 성은 보존된다.
    --> param : const char*, T: const char*

    배열 인수

    함수에 배열을 전달하는 것은 배열이 포인터로 붕괴되는 결과를 가져온다.

    이때문에 배열의 크기를 알수 없게 되는데, 다음과 같이 참조로 전달하면 

    template<typename T>
    void f(T& param);

    const char name[] = "FUCK";

    f(name);

    T는 const char [4]로, param은 const char (&) [4]로 연역된다. 따라서 배열의 크기를 알수 있다.

    이를 이용해서 다음과같이 배열에 담긴 원소의 개수를 연역하는 템플릿을 만들 수 있다.

    template<typename T, std::size_t[N]
    constexpr std::size_t arraySize(T (&) [N]) noexcept
    {
         return N;
    }

    constexpr로 선언하면 함수 호출의 결과를 컴파일 도중에 사용할 수 있게 된다. 따라서 다음과같이 사용할 수 있다.
    int keyVals[] = { 1, 3, 5, 7, 9, 11 };
    int mappedVals[arraySize(keyVals)];


Designed by Tistory.