[effective modern c++] 항목 3: decltype의 작동 방식 숙지

2016. 7. 16. 19:01프로그래밍/Effective Modern C++

728x90
728x90

decltype

주어진 이름이나 표현식의 형식을 알려준다.


C++11에서 decltype는 함수의 반환 형식이 그 매개변수 형식들에 의존하는 함수 템플릿을 선언할 때 주로 쓰인다.


너무 이해 안 되서 한 10번은 넘게 읽은 것 같다.


예시로 이해해보기

1
2
3
4
5
    std::deque<int> intDq;
    intDq.resize(10);
 
    authAndAccess(intDq, 5= 10;
    std::cout << intDq.at(5<< std::endl;;
cs


기본 적으론 해당 문장을 컴파일 하기 위해서이다.

authAndAccess 함수로부터 intDq의 원소의 레퍼런스를 받아 값을 바로 넣는 것이다.


template로 선언하면

1
2
3
4
5
6
7
8
9
10
11
/* 
    template<class Container, class Index>
    auto authAndAccess(Container& c, Index i)
    {
        // authenticateUser();
        return c[i];
    }
    // authAndAccess(intDq, 5) = 10; // error
    // 심각도    코드    설명    프로젝트    파일    줄    비표시 오류(Suppression) 상태
    // 오류    C2106    '=': 왼쪽 피연산자는 l-value이어야 합니다.    Test    c:\users\jo minhyuk\documents\visual studio 2015\projects\test\test\test.cpp    39    
*/
cs


이런 꼬라지가 될 텐데. 실제로 컴파일을 해보면

return c[i] 에서 auto 연역을 통해  우측값이 변환되어 나오므로 정상적으로 컴파일이 되지 않는다.

실제로 l-value 관련 에러가 난다.

intDq[5]를 통해 int&를 돌려주나, authAndAccess에 대한 

auto 반환 형식 연역 과정에서 참조가 제거되기 때문에 결국 반환 형태는 int가 된다.


그래서 decltype(auto) 라는 이상한 형태의 문법이 생겨나는데..

auto는 해당 형식이 연역되어야 하며, decltype는 그 연역 과정에서 decltype 형식 연역 규칙을 적용하겠다는 의미


위의 해결 코드의 문제점은 우측값을 전달할 수 없다는 것이다.

라고는 하는데 실제로 해보니 정상동작 하였다.

예시에 나온 팩토리 함수에서 리턴 받은 객체를 통해 접근하여 값을 복사했지만 정상동작....


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::deque<int> makeIntDque()
{
    std::deque<int> dq;
    dq.resize(10);
 
    static int i = 1000;
    for (auto& value : dq) {
        value = i++;
    }
 
    return dq;
}
 
-- main
 
    auto copyFuncRtValue = authAndAccess(makeIntDque(), 5);
    std::cout << copyFuncRtValue << std::endl;
cs


이상이 없었지만 책에서 제안하는 방법은


1
2
3
4
5
6
template<class Container, class Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
    // authenticateUser();
    return std::forward<Container>(c)[i];
}
cs


좌측값 뿐만 아니라 우측값도 받아들일 수 있도록 선언을 고치는 것이다.

이를 보편 참조를 통해 좌측값과 우측값을 함께 관리하는 함수를 하나로 통합할 수 있다.

보편 참조에 std::forward를 붙인다.(오른 값에는 move, 보편 참조는 forward)


위는 C++14의 최종 버전이다. 아래는 C++11의 최종 버전.


1
2
3
4
5
6
template<class Container, class Index>
auto authAndAccess(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i])
{
    // authenticateUser();
    return std::forward<Container>(c)[i];
}
cs


주의

1
2
3
4
5
6
7
8
9
10
11
12
13
decltype(auto) f1()
{
    int x = 0;
    ...
    return x;
}
 
decltype(auto) f2()
{
    int x = 0;
    ...
    return (x);
}
cs


x 와 (x)는 decltype에서 전자는 int를 반환 후자는 int&를 반환한다.

지역 변수의 레퍼런스를 리턴하기때문에 망할 수 있다. 주의해야 한다.


기억해 둘 사항들

- decltype는 항상 변수나 표현식의 형식을 아무 수정 없이 보고한다.

- decltype는 형식이 T이고 이름이 아닌 좌측값 표현식에 대해서는 항상 T& 형식을 보고한다.(중요)

- c++14는 decltype(auto)를 지원한다. decltype(auto)는 auto처럼 초기치로부터 형식을 연역하지만,

그 형식 연역 과정에서 decltype의 규칙을 적용한다.


728x90
반응형