KoreanFoodie's Study

Effective Modern C++ | 항목 28 : 참조 축약을 숙지하라 본문

Tutorials/C++ : Advanced

Effective Modern C++ | 항목 28 : 참조 축약을 숙지하라

GoldGiver 2022. 10. 26. 10:03

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 28 : 참조 축약을 숙지하라

핵심 :

1. 참조 축약은 템플릿 인스턴스화, auto 형식 연역, typedef 과 별칭 선언의 지정 및 사용, decltype 의 지정 및 사용이라는 네 가지 문맥에서 일어난다.
2. 컴파일러가 참조 축약 문맥에서 참조에 대한 참조를 만들어내면, 그 결과는 하나의 참조가 된다. 원래의 두 참조 중 하나라도 왼값 참조이면 결과는 왼값 참조이고, 그렇지 않으면 오른값 참조이다.
3. 형식 연역이 왼값과 오른값을 구분하는 문맥과 참조 축약이 일어나는 문맥에서 보편 참조는 오른값 참조이다.


우리는 T&& 같은 형식에 대해 컴파일러가 타입을 어떻게 추론하는지를 지금까지 지켜봐왔다. C++ 에서는 참조에 대한 참조(auto& &rx = x;) 선언은 위법이지만, 보편 참조의 경우, 참조에 대한 참조가 허용된다. 사실 참조 축약을 요약하면 다음과 같다.

만일 두 참조 중 하나라도 왼값 참조이면 결과는 왼값 참조이다. 그렇지 않으면(즉, 둘 다 오른값 참조이면) 결과는 오른값 참조이다.

std::forward 가 작동하는 것도 이 참조 축약 덕분이다. 예시를 보자.

template<typename T>
T&& forward(typename
  remove_reference<T>::type& param)
{
  return static_cast<T&&>(param);
}

위에서, forward 함수에 Widget 형식의 왼값이 전달되면, T 는 Widget& 로 연역된다. 인스턴스화된 버전은 다음과 같을 것이다.

Widget& && forward(typename
  remove_reference<Widget&>::type& param)
{
  return static_cast<Widget& &&>(param);
}

참조 축약과 std::remove_reference 가 적용된 버전은 다음과 같다.

Widget& forward(Widget& param)
{
  return static_cast<Widget&>(param);
}

오른값일 경우, 오른값으로 캐스팅이 진행될 것이다.

참조 축약이 일어나는 문맥은 네 가지이다.

1. 템플릿 인스턴스화

2. auto 변수에 대한 형식 연역
예시를 보자.

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

// 오른값을 돌려주는 함수
Widget widgetFactory();

// 변수 (왼값)
Widget w;

// 왼값으로 호출, T 는 Widget&
func(w);

// 오른값으로 호출, T 는 Widget&&
func(widgetFactory());

...

auto&& w1 = w;

auto&& w2 = widgetFactory();

위 코드에서 w1 과 w2 는 각각 어떤 형식으로 연역될까? w1 의 경우, w 가 왼값이므로 "Widget& &&" 가 되어 결국 Widget& 타입이 된다. w2 의 경우, "Widget&&" 이 되므로, 그냥 오른값 참조가 될 것이다.


3. typedef 과 별칭 선언(using, 항목 9 참고)의 지정 및 사용

4. decltype 사용(항목 3 참고)

Comments