C++ 중급 - Forwarding Reference

타입·2024년 2월 20일
0

C++ 공부

목록 보기
13/17

Forwarding (universal) Reference

  • 함수 파라미터의 모양
    • int&: int타입의 lvalue만 전달 가능
    • int&&: int타입의 rvalue만 전달 가능
    • T&: T 자체가 참조를 포함하고 있다면 reference collapsing 규칙을 적용, 결국 lvalue reference
  1. 사용자가 템플릿 인자를 직접 전달하는 경우
    사용자가 전달한 타입을 적용해서 함수가 생성 (함수의 인자는 상관없음)
template<typename T> void f3(T& arg) {}

int main()
{
    int n = 0;

    f3<int>(n);     // T : int      T& : int&       f3(int& arg) 함수 생성
    f3<int&>(n);    // T : int&     T& : int& &     f3(int& arg) 함수 생성
    f3<int&&>(n);   // T : int&&    T& : int&& &    f3(int& arg) 함수 생성
}
  1. 사용자가 템플릿 인자를 전달하지 않는 경우
    함수의 인자를 받을 수 있도록 T의 타입을 결정
template<typename T> void f3(T& arg) {}

int main()
{
    int n = 0;
    
    // T를 int, int&, int&& 뭘해도 rvalue를 받을 수 없음
    // T를 int로 결정하고 함수를 생성하지만 컴파일 에러
    f3(0);  // error
    
    // T가 int, int&, int&& 뭐든지 lvalue를 받을 수 있음
    // T를 int로 결정
    f3(n);  // ok
}

결론, T&는 임의 타입의 lvalue만 전달 가능


Forwarding (universal) Reference 2

template<typename T> void f4(T&& arg) {}

int main()
{
    int n = 0;

    // 1. 사용자가 템플릿 인자를 직접 전달하는 경우
    f4<int>(0);   // T=int     T&&=int&&     f4(int&& arg)
    f4<int&>(n);  // T=int&    T&&=int& &&   f4(int&  arg)
    f4<int&&>(0); // T=int&&   T&&=int&& &&  f4(int&& arg)

    // 2. 사용자가 템플릿 인자를 전달하지 않은 경우
    f4(n);  // T=int&   f4(int& arg)
    f4(0);  // T=int    f4(int&& arg)
}

lvalue(n)를 받을 수 있는 함수가 생성되고,
rvalue(0)를 받을 수 있는 함수가 생성된다는 의미

int&: lvalue reference
int&&: rvalue reference
T&: lvalue reference
T&&: forwarding reference (universal reference)

  • 템플릿 함수에서 생성된 함수의 모양을 확인해 보려면
    컴파일러가 지원하는 매크로 사용 __FUNCSIG__

  • 함수 템플릿을 만들 때 forwarding reference를 사용하면
    lvalue와 rvalue를 각각 받을 수 있는 함수 생성
    생성된 각 함수는 call by value가 아닌 call by reference를 사용해서 전달받음


lvalue와 rvalue 모두 받을 수 있는 함수 만들기

  1. call by value
    인자로 전달된 객체의 복사본이 생성

    void foo(int arg) {}
  2. const lvalue reference
    복사본은 생성되지 않지만 const 속성을 추가해서 가리킴

    void foo(const int& arg) {}
  3. lvalue 버전과 rvalue 버전의 함수를 따로 제공
    복사본도 없고 const 속성도 추가되지 않음
    함수 인자로 전달된 객체의 속성의 변화없이 받을 수 있음

    void foo(int&  arg) {}    
    void foo(int&& arg) {}
  4. forwarding reference 사용
    방법 3의 함수들을 컴파일러가 자동 생성

    template<typename T> void foo(T&& arg) {}
  • forwarding reference의 의미
    임의 타입의 lvalue와 rvalue를 복사본을 만들지 않고 속성의 변화 없이 그대로 받고 싶을 때 사용

  • forwarding reference가 활용되는 주된 분야

    • Move Semantics
    • Perfect Forwarding

forwarding reference 사용 시 주의사항

  • forwarding reference를 사용하려면
    함수 자체가 템플릿이어야 함
    foo 함수는 함수 템플릿이 아닌 클래스 템플릿의 멤버 함수
template<typename T> class Test 
{
public:
    void foo(T&& arg) {}
    template<typename U> void goo(U&& arg) {}
};

int main()
{
    int n = 0;

    Test<int> t;    // T=int로 이미 결정
                    // void foo(int&& arg)

//	t.foo(n); // error
    t.foo(0); // ok

    t.goo(n);
    t.goo(0);
}
  • auto 와 forwarding reference
    auto는 template와 type deduction 규칙이 동일
    • auto&: lvalue reference
    • auto&&: forwarding reference
int main()
{
    int n = 0;

    auto a1 = n; // ok
    auto a2 = 0; // ok

    auto& a3 = n; // ok
//	auto& a4 = 0; // error

	// auto 자리는 T로, 우변을 함수인자로 생각
    // T&& arg = 함수인자
    auto&& a5 = n; // auto=int&		int& && a5 = n -> int&  a5 = n
    auto&& a6 = 0; // auto=int		int &&  a6 = 0 -> int&& a6 = n
}
profile
주니어 언리얼 프로그래머

0개의 댓글