KoreanFoodie's Study
Effective Modern C++ | 항목 3 : decltype 의 작동 방식을 숙지하라 본문
Effective Modern C++ | 항목 3 : decltype 의 작동 방식을 숙지하라
GoldGiver 2022. 10. 26. 09:49
C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!
항목 3 : decltype 의 작동 방식을 숙지하라
핵심 :
1. decltype 은 항상 변수나 표현식의 형식을 아무 수정 없이 보고한다.
2. decltype 은 형식이 T 이고 이름이 아닌 왼값 표현식에 대해서는 항상 T& 형식을 보고한다.
3. C++14 는 decltype(auto) 를 지원한다. decltype(auto)는 auto 처럼 초기치로부터 형식을 연역하지만, 그 형식 연역 과정에서 decltype 의 규칙들을 적용한다.
컨테이너의 operator[ ] 반환 형식을 손쉽게 표현해 보자.
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
-> decltype(c[i])
{
authenticateUser();
return c[i];
}
이름 앞에 auto 를 지정하는 것은 형식 연역과는 관련이 없다. 여기서의 auto 는 C++11 의 후행 반환 형식(trailing return type) 구문이 쓰인다는 점을 나타내는 것이다(반환 형식을 매개변수 목록 다음에서 정함).
C++14 부터는 이렇게 간단하게 만들 수도 있다.
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
{
authenticateUser();
return c[i];
}
이 경우에는 반환 형식이 c[i] 로부터 연역된다. 그런데 이 경우, 다음과 같은 코드는 컴파일되지 않는다.
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
{
return c[i];
}
int main()
{
std::deque<int> d{1,2,3,4,5,6};
authAndAccess(d, 5) = 10;
}
왜냐하면, 형식을 연역하는 과정에서 Container 타입이 참조자가 제거되어 반환 형식은 int 가 되기 때문이다. 즉, 함수의 반환값으로서의 이 int 는 오른값이며, 결과적으로 위의 코드는 오른값 int 에 10을 배정하려 해서 컴파일이 되지 않는 것이다.
위 코드는 다음과 같이 고치면 된다. 함수의 반환 형식에 decltype 형식 연역이 적용되게 만드는 것으로, autoAndAccess 가 c[i] 의 반환 형식과 정확히 동일한 형식을 반환하게 만드는 것이다!
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i)
{
return c[i];
}
decltype(auto) 는 다음과 같은 일반적인 상황에서도 유용하다.
class Widget {};
int main()
{
Widget w;
const Widget& cw = w;
// auto 형식 연역 : Widget
auto myWidget1 = cw;
// decltype 형식 연역 : const Widget&
decltype(auto) myWidget2 = cw;
}
만약 authAndAccess 가 왼값 참조 뿐만이 아니라 오른값 참조도 받을 수 있게 만드려면 어떻게 해야 할까? 간단한 해결책은, 다음과 같이 보편 참조를 활용하는 것이다.
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
return c[i];
}
// 팩터리 함수
std::deque<std::string> makeStringDeque();
...
// 팩터리 함수가 만든 오른값 deque 의 다섯 번째 원소의 복사본을 만든다
auto s = autoAndAccess(makeStringDeque(), 5);
마지막으로, 항목 25 의 조언에 따라 보편 탐조에 std::forward 를 적용하면 끝이다.
// C++14 이상
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
return std::forward<Container>(c)[i];
}
// C++11 버전
template<typename Container, typename Index>
auto authAndAccess(Container&& c, Index i)
-> decltype(std::forward<Container>(c)[i])
{
return std::forward<Container>(c)[i];
}
마지막으로, decltype 이 예상 밖의 결과를 제공하는 한 가지 경우를 보자.
decltype(auto) f1()
{
int x = 0;
// decltype(x) 는 int 이므로 int 를 반환
return x;
}
// C++ 는 (x) 를 왼값으로 정의한다!
decltype(auto) f2()
{
int x = 0;
// decltype((x)) 는 int& 이므로 int& 를 반환
return (x);
}
따라서, decltype(auto) 를 사용할 때는 주의해야 한다.
'Tutorials > C++ : Advanced' 카테고리의 다른 글
Effective Modern C++ | 항목 5 : 명시적 형식 선언보다는 auto 를 선호하라 (0) | 2022.10.26 |
---|---|
Effective Modern C++ | 항목 4 : 연역된 형식을 파악하는 방법을 알아두라 (0) | 2022.10.26 |
Effective Modern C++ | 항목 2 : auto 의 형식 연역 규칙을 숙지하라 (0) | 2022.10.26 |
Effective Modern C++ | 항목 1 : 템플릿 형식 연역 규칙을 숙지하라 (0) | 2022.10.26 |
Effective C++ | 항목 55 : Boo子有親! 부스트를 늘 여러분 가까이에 (0) | 2022.10.26 |