본문 바로가기
Language/C++

이동생성자와 보편참조법(universal ref)

by W00gie 2022. 1. 7.

https://dev-record.tistory.com/56?category=956403 

 

lvalue와 rvalue + 우측값 참조법

* lvalue와 rvalue란? C++ 내에서 모든 식들은 카테고리라는 부가적인 정보를 포함하고 있다. l-value와 r-value가 이에 해당되는데, lvalue는 읽고 대입할 수 있는 값, 식(expression)에서 좌측에 존재하기에

dev-record.tistory.com

지난 포스팅 '우측값 참조법'이후의 공부 내용이다. 이번 포스팅에서는 rvalue 참조와 관련된 추가 내용을 정리한다.

 

이동생성자 (rvalue)

이동생성자는 앞서 말한 복사생략 (copy ellision)을 수행하기 위해 정의하는 생성자이다. 지난 포스팅에서는 실체가 없는 rvalue와 이를 참조하는 rvalue 참조방식 (&&)에 대해 알아봤는데, 이를 이용한 생성자이다. 해당 생성자를 작성해야 기본 대입식에서 move(이동)가 가능하다.

 

Player::Player(Player&& p1) {
  _playerName = p1._playerName;
  _class = p1._class;
  _Hp = p1._Hp;

  // 함수 스택 해제 시 객체가 삭제되지 않게끔
  p1._playerName = nullptr;
}

이동생성자의 정의와 함께 소멸자에도 소멸자의 조건을 수정해줘야 대입식에서 정상적으로 이동이 수행된다.

Player::~Player() { 
	//소멸자의 조건 수정 - nullptr 이 아닐때만
  if (_playerName) delete[] _playerName;
}

 

 

보편참조 (universal reference)

 

&&이 앞서 rvalue의 참조법임을 알았지만, &&에는 한 가지 더 의미가 있는데 이를 '보편 참조'라 정의한다. 보편참조는 오른값 참조 혹은 왼값 참조 중 하나라는 의미이다. 보편참조를 하는 케이스는 다음과 같은 경우이다.

 하나는 template 을 사용하는 경우이고 나머지 하나는 auto 를 사용하는 경우이다. 두가지 모두 형식 연역이 일어난다는 공통점이 있다. 하지만 몇가지 주의할 점이 있다. 형식 연역이 일어나도 보편 참조가 아닌 경우가 있다. 아래의 예시를 참조하면 쉽게 이해할 수 있다.

void f(Widget&& param);				//1 오른값 참조
 
Widget&& var1 = Widget();			//2 오른값 참조
 
auto&& var2 = var1;					//3 보편 참조
 
template<typename T>
void f(std::vector<T>&& param);			//4 오른값 참조
 
template<typename T>
void f(T&& param);					//5 보편 참조

보편참조가 발생하는 경우 lvalue와 rvalue 모두 보편참조를 수행하는 함수를 우선적으로 수행한다. 보편 참조를 수행하는 케이스의 경우 왼값에 대한 별도의 함수를 정의할 필요가 없다 (보편 참조를 하는 함수에 왼값과 오른값이 모두 먹힌다)

 +) 반대의 케이스로 왼값과 오른값을 구분하여 수행해야 하는 함수를 작성할 경우 보편 참조에 필수적으로 필요한 형식 면역을 없애기 위해 직접 파라미터 타입을 명시해주거나 const 를 파라미터에 붙여주면 된다.