본문 바로가기

Programming/CPP11&14

[C++11] RValue Reference(3)

반응형

5. Forwarding Problem과 Perfect Fowarding

Fowarding Problem은 레퍼런스 타입을 매개 변수로 갖고 있는 제너릭 함수에서 발생할 수 있습니다.

레퍼런스 타입의 매개변수를 다른 함수에 전달해 주는 과정에서 발생할 수 있습니다.

예제를 먼저 확인하겠습니다.

struct W
{
	W(int&, int&){}
};

struct X
{
	X(const int&, int&){}
};

struct Y
{
	Y(int&, const int&){}
};

struct Z
{
	Z(const int&, const int&){}
};

template <class T, class A1, class A2>
T* factory(A1& a1, A2& a2)
{
	return new T(a1, a2);
}

int main()
{
	int a = 4, b = 5;
	W* pw = factory<W>(a, b);

	// error C2664: 'factory' : cannot convert parameter 1 from 'int' to 'int &'
	// Z* pz = factory<Z>(2, 2);

	delete pw;

	return 0;
}

W, X, Y, Z가 각각 const int&와 int&를 생성자에서 받게 되어 있고

생성자를 호출해주는 함수 템플릿인 factory가 존재합니다.

main에서 W를 생성하는 경우 정상적으로 호출이 되지만 Z를 생성하는 경우에는 에러가 발생합니다.

factory 함수가 수정이 가능한 LValue Reference를 받게 되어 있는데 RValue를 전달하기 때문입니다.

기존에는 이런 문제를 해결하기 위해서 const T&와 T& 타입의 모든 조합으로 factory를 작성해야 했습니다.

예제와 같은 경우는 2개를 전달받기 때문에 다음과 같이 factory 함수의 4개의 조합이 필요합니다.

A1&, A2&
const A1&, A2&
A1&, const A2&
const A1&, const A2&

전달받는 인자의 수가 n개일 때 필요한 조합은 2^n으로 커집니다.

이렇게 참조 타입을 전달받는 템플릿 함수에서 다른 함수로 정상적으로 인자를 넘겨주지 못하는 것을

Fowarding Problem이라고 합니다.

이 문제를 해결하는 방법은 RValue Reference를 활용하는 것입니다.

template <class T, class A1, class A2>
T* factory(A1&& a1, A2&& a2)
{
    return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

A1과 A2의 RValue Reference를 전달받는 형식으로 함수가 변경되었습니다.

그리고 생성자에 std::foward라는 것을 사용해서 전달하고 있습니다.

std::foward는 static_cast를 통해서 T&& 타입으로 변환해주는 템플릿 함수입니다.

이전에 설명한 std::move와 함께 <utility> 헤더에 포함되어 있습니다.

템플릿을 위와 같이 작성하게 되면 W, X, Y, Z 모두를 호출할 수 있게 됩니다.

이것이 가능한 이유는 템플릿이 인자를 추론한 이후에, Reference Collapsing Rules을 사용하기 때문입니다.

그리고 RValue Reference의 특징 중 하나가 RValue가 RValue Reference로 참조가 가능하게 되면

LValue로 간주가 된다는 점입니다.

RValue Reference의 몇 가지 특징은 따로 정리를 하겠습니다.

최종적으로 변경된 함수 템플릿을 이용해서 다음과 같이 호출이 가능합니다.

int main()
{
	int a = 4, b = 5;
	W* pw = factory<W>(a, b);
	X* px = factory<X>(2, b);
	Y* py = factory<Y>(a, 2);
	Z* pz = factory<Z>(2, 2);

	delete pw;
	delete px;
	delete py;
	delete pz;

	return 0;
}

하나의 함수 템플릿을 이용하여 정상적으로 호출이 가능하게 되었습니다.

RValue Reference를 통해 완벽한 전달(Perfect Fowarding) 구현 가능합니다.

반응형

'Programming > CPP11&14' 카테고리의 다른 글

[C++11] final과 override  (0) 2014.12.21
[C++11] RValue Reference(4)  (0) 2014.12.18
[C++11] RValue Reference(2)  (0) 2014.12.17
[C++11] RValue Reference(1)  (0) 2014.12.14
[C++11] 이름 없는 함수, 람다(Lambda)(2)  (2) 2014.12.11