본문 바로가기
Language/effective modern C++

Effective modern C++ ( 형식 언어 )

by y.j 2021. 9. 26.
728x90

항목 3 : decltype의 작동 방식을 숙지하라

auto, decltype은 operator[]에 따라 다른 값을 돌려 줄 수 있다. ( 왜 그런지는 항목 6에서 확인 )

Widget w;

auto myWidget1& cw = w;

auto myWidget1 = cw;                 // auto의 형식 연역 :
                                     // myWidget1의 형식 Widget
decltype(auto) myWidget2 = cw;       // decltype 형식 연역 :
                                     // const Widget&

그렇다면 이 2개를 어떻게 맞출 수 있을까?
C++11 : 후행 반환 형식(trailing return type)을 통해 auto의 타입형식을 decltype과 일치 시킬 수 있다.

// 아래 코드는 정련 할 필요가 있다.
template<typename Container, typename Index> 
auto autoAndAceess(Container& c, Index i) 
 -> decltype(c[i])
{
    authenticateUser();
    return c[i];
}

C++14

// C++14 아주 정확한 것은 아님
template<typename Container, typename Index>    // 정련이
auto authAndAceess(Container& c, Index i )      // 필요함
{
    authenticateUser();
    return c[i];             // 반환 형식은 c[i]로부터 연역된다.
}

std::deque<int> d;

authAndAccess(d, 5) = 10; // 이 코드는 컴파일이 되지 않는다.

여기서 auto는 int 반환한다. d[5]는 int&를 반환하지만, auto는 &를 무시하기 때문이다.
C++14는 decltype(auto)를 통해 int&를 연역 할 수 있다.
auto는 해당 형식이 연역되어야 함을 뜻하고
decltype은 형식 연역 규칙들이 적용되어야 함을 뜻한다.

위에 선언을 정련해보자!

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i);

위의 선언방식에 Container& c는 l-value 참조이기 때문에 r-value를 넣을 수 없다.
하지만 넣을 경우를 대비하여 보편참조로 바꾸는 것이 옳다!

// C++14
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i) // 보편참조로 바꿈
{
    authenticateUser();
    return std::forward<Container>(c)[i];           // return 바꿈
}

// C++11
template<typename Container, typename Index>
auto authAndAccess(Container&& c, Index i)
-> decltype(std::forward<container>(c)[i]           
{
    authenticateUser();
    return std::forward<Container>(c)[i];           
}

또, 이 함수의 return값은 색인 값을 쓰는 방식으로 되어 있기 때문에 return 값도 바꿔주는 것이 더 좋다.

예상 밖의 결과를 가져 올 수 있는 경우
이름보다 복잡한 표현식이 되는 경우 원하지 않은 타입이 연역될 수 있다.

 int x = 10;
 decltype(x);            // int;
 decltype((x));          // int&;
728x90

댓글