C++ Chapter 15.2 : R-Value References

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 15. 의미론적 이동과 스마트 포인터

15.2 R-Value References

#include <iostream>
using namespace std;

void doSomething(int& lref) { cout << "L-value ref" << endl; }
void doSomething(int&& rref) { cout << "R-value ref" << endl; }
int getResult() { return 100; }

int main()
{
	int x = 5;
	int y = getResult();
	const int cx = 6;
	const int cy = getResult();

	/* L-value references */
	int& lr1 = x;		/* ⭕ x는 int형 변수이므로 참조가 가능 */
	int& lr2 = cx;		/* ❌ cx는 const int형 변수이므로 변조 가능성 때문에 참조 불가능 */
	int& lr3 = 10;		/* ❌ 상수도 const int이므로 참조가 불가능 */

	const int& lr4 = x;		/* ⭕ 참조 대상을 x에서 바꿀 수 없다는 의미이므로 가능 */
	const int& lr5 = cx;	/* ⭕ 참조 대상을 cx에서 바꿀 수 없다는 의미이므로 가능 */
	const int& lr6 = 10;	/* ⭕ 참조 대상을 10에서 바꿀 수 없다는 의미이므로 가능 */

	/* R-value references */
	int&& rr1 = x;		/* ❌ x는 int형 변수이므로 R-value 참조가 불가능 */
	int&& rr2 = cx;		/* ❌ cx는 const int형 변수이므로 메모리에 남아있으므로 R-Value 참조 불가능 */
	int&& rr3 = 10;		/* ⭕ 상수는 R-value이므로 참조 가능 */
	int&& rr4 = getResult();/* ⭕ return value도 곧 사라질 운명인 R-vale이므로 참조 가능*/
	rr3 = 20;				/* 값을 바꿀수도 있다. */

	const int&& rr5 = x;	
	const int&& rr6 = cx;
	const int&& rr7 = 10;
	const int&& rr8 = getResult();

	doSomething(x);				/* L-value reference */
	doSomething(10);			/* R-value reference */
	doSomething(getResult());	/* R-value reference */
	doSomething(rr3);			/* L-value reference */
}

& 👉 L-Value를 참조한다.

  • 일반 변수같은 L-Value만 참조 가능하다.
  • const int & 같은 const L-value referenceL-value, R-value 둘 다 참조 가능하다.

&& 👉 잠시 동안만 저장해놓을 R-Value 인스턴스만 R-Value를 참조한다.

  • 메모리에서 잠깐 있다가 사라지는 R-Value만 참조 가능하다.
    • 일반 변수와 상수(const)는 L-Value이므로 참조 불가능 하다.
    • 상수 리터럴이나 함수 리턴 값 같이 메모리를 잠깐만 차지 하는 인스턴스만 참조 가능!
  • const int && 같은 const R-value reference 또한 R-Value만 참조 가능하다.


🔔 L-value 와 R-value

  • L-value
    • 어떤 메모리 공간을 차지하고 있음.
      • 따라서 주소를 가짐.
    • 왼쪽, 오른쪽에 모두 위치 가능
  • R-value
    • 잠깐 존재하고 해당 문장이 끝나면 사라질 운명.
      • 메모리 공간은 아주 잠시동안만 가진 후 사라진다.
    • 반드시 오른쪽에만 위치 가능.
      • 주소값을 취할 수 없기 떄문에.
        • &(R-Value) 👉 오류
	int& func1(int& a) { return a; } // int& 즉  L-Value Reference 리턴 
	int func2(int b) { return b; } // 그냥 일반적인 int 값 리턴

	int a = 5;
	int b = 7;

	func1(a) = 4; // 레퍼런스의 값을 4로 해라 의미로 실제 a의 값이 4가 됨
	a = func2(b); // 리턴 값은 이 문장 실행 될때만 잠깐 존재할 뿐 문장 실행이 끝나면 사라짐

	std::cout << &func1(a) << std::endl;  // ⭕가능
	std::cout << &func2(b) << std::endl;  // ❌오류 : R-value는 주소를 가질 수 없음.
  • 함수 func1 은 인수도 L-value Reference로 받고 리턴 타입이 L-value Reference이기 때문에
    • func1(a) = 4 👉 a를 L-value Reference 타입으로 리턴하기 때문에 func1(a)가 함수 리턴 값임에도 불구하고 L-value로서 작용한다.
      • 따라서 a 의 값이 4 로 바뀜
      • &func1(a) 이렇게 주소도 가질 수 있다.
  • 함수 func2 은 리턴 될 때 R-value로서 리턴된다.
    • 리턴되는 매개 변수 b 값이 임시 공간에 복사가 됨
      • 따라서 &func2(b) 이렇게 주소를 가질 수 없다.
    • 또한 매개변수 b 에 인수의 값이 복사가 된다.


🔔 L-value Reference

	/* L-value references */
	int& lr1 = x;			/* ⭕ x는 int형 변수이므로 참조가 가능 */
	int& lr2 = cx;		/* ❌ cx는 const int형 변수이므로 변조 가능성 때문에 참조 불가능 */
	int& lr3 = 10;		/* ❌ 상수도 R-Value인 const int이므로 참조가 불가능 */

	const int& lr4 = x;		/* ⭕ 참조 대상을 x에서 바꿀 수 없다는 의미이므로 가능 */
	const int& lr5 = cx;	/* ⭕ 참조 대상을 cx에서 바꿀 수 없다는 의미이므로 가능 */
	const int& lr6 = 10;	/* ⭕ 참조 대상을 10에서 바꿀 수 없다는 의미이므로 가능 */
  • L-Value 만 참조 가능하다.
  • & 하나를 사용하는 L-Value Reference 자체도 L-Value 이다.
    • int& lr1 = x 참조 가능 ⭕
      • x 는 const 가 아닌 변수.
      • lr1과 x는 동일한 메모리 공간을 가지게 되며 편하게 수정 가능해짐
    • int& lr2 = cx 참조 불가능 ❌
      • cx 는 const 형 변수.
      • lr2도 const여야 참조 가능함
    • int& lr3 = 10 참조 불가능 ❌
      • R-value는 참조할 수 없다. R-value는 메모리 공간을 가지고 있지 않기 때문에!
  • const 형 L-Value Reference는 R-value도 참조할 수 있다.
    • 메모리 공간은 잠깐 있다가 없어지긴 하지만 어차피 앞으로 수정 못하니까 그냥 symbolic constant 처럼 사용
    • const int& lr4 = x; 참조 가능 ⭕
    • const int& lr5 = cx; 참조 가능 ⭕
    • const int& lr6 = 10; 참조 가능 ⭕


🔔 R-value References

	/* R-value references */
	int&& rr1 = x;		/* ❌ x는 int형 변수(L-Value)이므로 R-value 참조가 불가능 */
	int&& rr2 = cx;		/* ❌ cx는 const int형 변수라 메모리에 남아있으므로 (L-Vaule) 참조 불가능 */
	int&& rr3 = 10;			/* ⭕ 상수는 R-value이므로 참조 가능 */
	int&& rr4 = getResult();/* ⭕ return value도 곧 사라질 운명인 R-value이므로 참조 가능*/

	rr3 = 20;				/* 값을 바꿀수도 있다. */
	cout << rr3 << endl; /* 출력할 수도 있다. */

	const int&& rr5 = x;	/* ❌ 참조 불가능 : x는 L-Value*/
	const int&& rr6 = cx; /* ❌ 참조 불가능 : cx는 L-Value*/
	const int&& rr7 = 10;  /* ⭕ 참조 가능 : 10은 R-Value*/
	const int&& rr8 = getResult();   /*참조 가능 : 함수 리턴값은 R-Value*/

&& 2개 쓰면 R-value References

  • 참조 대상이 오직 R-value여야만 한다.
    • 금방 사라질 인스턴스만 참조할 수 있음
  • Move Sementics를 사용할 수 있을지, 사용할 수 없을지를 판별 할 때 쓰인다.
    • 곧 사라질 애들이기 때문에 Move를 시켜도 아무 상관 없다.
  • R-Value Refrence 자체는 L-Value 이다. R-Value만 받는 기능이라고 헷갈리지 말기!
  • int&& rr3 = 10
    • 10 이라는 R-Value의 곧 사라질 임시 공간은 R-Value Reference타입인 rr3으로만 접근하고 수정할 수 있다는 의미가 강하다.
      • 따라서 rr3으로 참조 대상이 되는 R-Value인 값을 수정하거나 출력할 수 있다.


🔔 L-Value/R-Value reference 파라미터

void doSomething(int& lref) 👉 L-Value reference 를 파라미터로 받음

void doSomething(int&& rref) 👉 R-Value reference 를 파라미터로 받음

  • 컴파일러는 두 함수가 다르다고 인식 함. 즉 오버로딩으로 인식 함
    • &&&는 다르다고 인식 하여 파라미터 타입이 다르다고 인식하는 것.
  • L-Value reference 넘겨 받은 L-Value를 함수 밖에서 다시 쓰려고 하면 Move Semantic 문제가 생길 수 있다.
    • 함수의 지역변수인 L-Value reference은 넘겨 받은 L-Value의 공간과 동일한 공간을 참조하게 되는데 함수가 끝나면 지역 변수는 사라지기 때문에, 이와 동시에 그 공간마저 사라지는 함수 코드까지 있었다면 넘겨 받은 L-Value로 함수 밖에서 접근하려 하면 이미 사라진 공간을 접근하려는 것과 동일해지기 때문이다.
  • R-Value reference Move Semantic 문제 생길 일이 없다.
    • 애초에 R-Value 즉 잠시 동안만 저장해놓을 r-value 인스턴스만 참조가 가능하기 때문이다.
1. void doSomething(int& lref) { cout << "L-value ref" << endl; }
2. void doSomething(int&& rref) { cout << "R-value ref" << endl; }
doSomething(x);				/* x는 L-value 이므로 1함수가 호출되고 int& left = x 가 된다.  */  
doSomething(10);			/* 10은 R-value 이므로 2함수가 호출되고 int && rref = 10이 됨 */
doSomething(getResult());	/*getResult()은 R-value 이므로 2함수가 호출됨 */
doSomething(rr3);			/* L-value reference */


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기