dev/C++

Effective Modern C++ #33 std::forward 를 통해서 전달할 auto&& 매개변수에는 decltype 을 사용하라

dev_dev 2025. 6. 30. 21:27

33. std::forward 를 통해서 전달할 auto&& 매개변수에는 decltype 을 사용하라

C++14 에서 가장 exciting 한 기능은 매개변수에 auto를 사용할 수 있는 general lambda 기능이다.

람다 클로저 클래스의 operator 를 템플릿 함수로 만들어서 임의로 구현할 수 있다.

예시)

auto f = [](auto x){ return normalize(x); };

// 위 람다함수 auto 구현하는 법

class 컴파일러가_만든_이름 {
  public:
    template<typename T>
    auto operator()(T x) const {
      return normalize(x);
    }
    ...
};

이제 위 케이스에서 x 가 l-value 또는 r-value 인자로 전달된다고 가정하자.

매개변수 x 는 operator 에서 항상 l-value 이므로, 완벽 전달이 구현되지 않는다.

 

완벽 전달을 구현하기 위해, 람다 표현식에서 수정할 부분은 두가지이다.

  1. x 가 보편 참조 형태여야함
  2. x 를 std::forward 를 사용해서 함수에 전달해야함

예시)

auto f = [](auto&& x) { return normalize(std::forward<???>(x)); };

보편 참조 형태에 std::forward 를 사용했으나, std::forward 에 지정할 형식이 명확하지 않다.

일단 x 는 그대로 사용할 수는 없는데 매개변수는 항상 l-value 이기 때문이다

또한 람다 표현식에서는 형식 매개변수 T 가 없으므로, 템플릿 함수처럼 <T> 와 같이 사용할 수도 없다.

 

decltype 을 사용해서 매개변수 형식이 l-value 인지, r-value 인지 구분할 수 있다.

이는 T 대신 추론된 타입을 제공한다.

 

decltype 을 사용할 수 있는 이유를 알아보자.

Widget 타입에 대해, auto 기반 보편 참조를 하는 경우 x는 Widget&, Widget&& 의 형태가 될 수 있다.

 

l-value 전달 - x : Widget&

r-value 전달 - x : Widget&&

( r-value 를 비참조 형식 (Widget)으로 지정하는 관례를 따르지 않음 )

 

std::forward 의 구현 예시)

template<typename T>
T&& forward(remove_reference_t<T>& param) {
	return static_cast<T&&>(param);
}

l-value 가 전달되는 경우 인스턴스 예시)

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

// 참조 축약 적용
Widget& forward(Widget& param) {
	return static_cast<Widget&>(param);
}

l-value 는 완벽 전달된다.

 

r-value 가 전달되는 경우 인스턴스 예시)

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

// 참조 축약 적용
Widget&& forward(Widget& param) {
	return static_cast<Widget &&>(param);
}

r-value 도 완벽 전달된다.

 

따라서 람다 매개변수 x 인자가 l-value 또는 r-value 일 때, decltype(x) 도 l-value 또는 r-value 참조 형식을 산출한다.

결국 decltype 을 사용하여 x 의 선언된 타입 그대로 반환하는 완벽 전달을 구현할 수 있다.

 

일반적인 완벽 전달 람다 예시)

auto f = [](auto&&... xs) { return normalize(std::forward<decltype(x)s>(xs)...); };