INDIES

EMC++ Item 33 정리 본문

프로그래밍/EMC++

EMC++ Item 33 정리

jwvg0425 2015. 5. 17. 00:09

auto&&를 std::forward할 땐 decltype

C++ 14에 추가된 generic lambda라는 기능은 람다의 매개변수 정의에 auto를 쓸 수 있게 해주는 굉장히 흥미로운 기능이다. 이 기능의 구현은 람다의 클로져 클래스 내부 멤버 함수 operator()를 템플릿으로 만드는 것으로 이루어진다.

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

//내부 클로져 클래스의 구현
class Closure
{
public:
    template<typename T>
    auto operator()(T x) const
    { return func(normalize(x)); }
}

위 예제에서 람다는 단순히 인자 x를 normalize로 전달해주는 역할만을 한다. 이 때 normalize 함수의 인자가 lvalue일 때와 rvalue일 때 서로 다르게 동작하도록 작성되어있다면, 위 코드는 적절하게 작성되었다고 말하기 힘들어진다. x는 무조건 lvalue로 취급되어 lvalue 함수만 호출될 것이기 때문이다. 이 경우 앞선 항목들에서 다뤘던 perfect forwarding을 쓰는게 적합하다.

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

하지만 여기서 문제가 생겼다. forward를 하려면 타입 T를 넘겨야하는데, x의 타입 T가 뭔지 알 수가 없다. 람다에서는 auto로 표기를 하므로 타입을 가리킬 마땅한 명칭이 없는 것이다. 이 경우 item 3에서 소개한 decltype을 쓸 수 있다. decltype(x)를 쓰면 x의 타입을 얻을 수 있으니, 이 걸 쓰면 되지 않을까? x가 lvalue일 경우 decltype(x)는 T&가 되므로 x가 lvalue일 경우는 잘 동작할 것이다. 반면 x가 rvalue인 경우 decltype(x)는 T&&이 된다. 원래는 타입 T를 넘겼으니 조금 차이가 생기는 것이다. 그럼 x가 rvalue일 때 std::forward가 어떻게 동작할지 살펴보자.

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

위 forward 코드에 Widget타입의 rvalue를 포워딩했다고 하자. decltype에 의해 추론된 타입을 집어넣으면 아래와 같은 형태가 될 것이다.

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

여기서 reference collapsing 룰을 적용하면 최종 형태는 다음과 같이 된다.

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

이로부터 decltype을 써도 우리가 원하는 결과를 얻을 수 있다는 결론을 얻을 수 있다. 이 결론을 앞의 람다 표현식에 적용시켜보자.

auto f =
      [](auto&& param)
      {
          return func(normalize(std::forward<decltype(param)>(param)));
      };

이 경우는 인자가 하나인 경우지만, C++14의 람다는 variadic까지도 지원한다. variadic의 경우 아래 코드와 같은 방식으로 작성하면 된다(pack 들어가는 것 말곤 차이도 없다).

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


'프로그래밍 > EMC++' 카테고리의 다른 글

EMC++ Item 34 정리  (0) 2015.05.17
EMC++ Item 32 정리  (0) 2015.05.17
EMC++ Item 31 정리  (0) 2015.05.17
EMC++ Item 30 정리  (0) 2015.05.13
EMC++ Item 29 정리  (0) 2015.05.13