C++ Chapter 15.2 : R-Value References
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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 reference는 L-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 에 인수의 값이 복사가 된다.
- 리턴되는 매개 변수 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는 메모리 공간을 가지고 있지 않기 때문에!
- int& lr1 = x 참조 가능 ⭕
- 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 */
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기