글 작성자: Sowhat_93

언제 std::move를 사용해야 하는지, 언제 std::forward를 사용해야 하는지 알아보는 시간을 가져보도록 하겠습니다.

 

1. std::move

 

std::move의 경우 형변환 작업을 수행합니다.

받은 param에 대해 R-value reference를 반환합니다.예를 들면 이런 식 입니다.

#include <iostream>

void Function(int&& Arg)
{
	std::cout << "R value Ref!" << std::endl;
}
int main()
{
	int i = 0;

	Function(i);
	//i는 L value입니다.
	//컴파일 오류가 납니다.
	Function(std::move(i));
	//이건 좋습니다.
	//std::move 가 R-value 형으로 바꿔서 리턴합니다. 
}

하나만 더 해보겠습니다.

#include <iostream>

void Function(int& Arg)
{
	std::cout << "L value Ref! " << std::endl;
}

void Function(int&& Arg)
{
	std::cout << "R value Ref!" << std::endl;
	Function(Arg);
}

int main()
{
	int i = 0;
	Function(std::move(i));
}

 

실행 결과는 어떻게 될까요?얼핏보면 무한 재귀처럼 동작할 것 같지만 그렇지 않습니다.

파라미터로 R value Reference를 취한다는 것은호출 당시 넘겨준 형태가 R value Reference 일때 이 함수가 실행되게 하겠다 정도 입니다.파라미터가 함수 내부 취급되는 방식은 어디까지나 지역변수입니다.함수 내부에서의 취급은 별반 달라지지 않습니다.따라서 저렇게 L value Reference로 인식하는 것입니다.

 

그래서 아래처럼 L value Reference를 받는 함수를 아예 없애면 컴파일이 안됩니다.

#include <iostream>
void Function(int&& Arg)
{
	std::cout << "R Value Ref!" << std::endl;
	Function(Arg);
	//L Value Reference 받는 함수가 없습니다.
	//컴파일 에러
}

int main()
{
	int i = 0;
	Function(std::move(i));
}

이런 상황에서 R value Reference를 인자로 받는 함수를 호출하려면 다시 한번 std::move를 호출 해줍니다.

void Function(int&& Arg)
{
	std::cout << "R Value Ref!" << std::endl;
	Function(Arg);
	//L Value Reference 받는 함수가 없습니다.
	//컴파일 에러
	Function(std::move(Arg));
	//이건 오케이입니다.
	//파라미터로 만들어지면서, L Value Ref로 취급되는 Arg를
	//다시한번 std::move를 통해 R value Ref 취급하도록 하는 것입니다.
}

 

2. std::forward

std::forward는 다음과 같은 상황에서 사용합니다.

#include <iostream>

template<class T>
void TemplateFunction(T&& Arg)
{
	Function(Arg);
}

int main()
{
	int i = 0;
	TemplateFunction(i + 3);
	//R value Ref를 받으니 전달이 당연히 가능합니다.
	TemplateFunction(i);
	//템플릿 함수의 경우는 R value reference 인자를 받도록 되어 있어도
	//L value 전달이 가능합니다.
}

템플릿 함수의 파라미터가 R value Reference로 되어있다면,

L value Reference 또한 전달할 수 있습니다.

(일반함수는 안됩니다.)

스캇 마이어스가 언급한 universal reference에 의한 타입 붕괴 상황이 바로 이 상황입니다.

템플릿 함수이기 때문에 타입을 컴파일러 마음대로 추론해버리는 상황입니다.

template<class T>
void TemplateFunction(T&& Arg)
{
	SomeFunction(std::move(Arg));
}
template<class T>
void TemplateFunction(T&& Arg)
{
	SomeFunction(Arg);
}

물론 템플릿 함수 또한  두개로 만들어 버리면 해결이 되기는 합니다만,

그런데 같은 일을 하는 함수를 두개나 만들기가 싫다면, 이때에 std::forward를 사용합니다.

#include <iostream>

void SomeFunction(int&& Arg)
{
	std::cout << "R Value Ref!" << std::endl;
}

void SomeFunction(int& Arg)
{
	std::cout << "L Value Ref!" << std::endl;
}

template<class T>
void TemplateFunction(T&& Arg)
{
	SomeFunction(std::forward<T>(Arg));
}

int main()
{
	int i = 0;
	TemplateFunction(i + 3);
	TemplateFunction(i);
}

std::forward를 사용하니 L value Reference 인자를 위한 함수를 하나 더 만들지 않고도 해결이 가능해 졌습니다.

'C++' 카테고리의 다른 글

[C++] STL Custom Allocator 사용  (0) 2022.04.08
[C++] C++ 11 mutable  (0) 2022.03.28
[C++] C++11 override, final  (0) 2022.03.28
[C++] new[] , delete[] 오버로딩시 주의 사항.  (0) 2022.03.28
[C++] const lvalue reference, rvalue reference  (2) 2022.03.23