좌측값( l-value )과 우측값( r-value )
좌측값과 우측값이란 말은 대입 연산자 '='의 왼쪽에 위치하느냐 오른쪽에 위치하느냐에 따라 나눠지는 말입니다.
int A;
10 = A;
위의 코드를 보면, 대입 연산자의 왼쪽에 위치한 10이 좌측값이고 A가 우측값입니다.
그런데, 실제로 컴파일해 보면 오류가 나면서 컴파일러는 다음의 오류 메시지를 띄웁니다.
error: lvalue required as left operand of assignment 10 = A;
번역하면, "대입의 왼쪽 피연산자는 좌측값 l-value 이어야 합니다"라는 뜻입니다.
좌측값 10이 있는데 뭐가 문제일까요?
왜냐하면, 이것은 10 = A 구문이 의미가 없는 구문이기 때문입니다.
C++에서 의미 있는 구문은
값을 받아들일 수 있는 표현 = 값
의 형태를 갖추어야 합니다.
정리하자면, 좌측값은 말 그대로의 "="의 왼쪽에 위치한 표현을 가리키는 것이 아니라,
값을 받아들일 수 있는 표현을 말하는 것입니다.
좀 더 구체적으로 말하자면, 값을 넣을 수 있는 메모리 위치를 나타내는 표현이라고 할 수 있습니다.
반대로, 우측값은 값 역할을 할 수 있는 표현을 말하는 것입니다.
모든 좌측값은 값을 가지고 있으므로 우측값이 될 수 있습니다.
그러나, 모든 우측값이 좌측값인 것은 아닙니다.
10 = A; // 10은 우측값이지만 좌측값은 될 수 없다
그럼, 다양한 구문을 통해서
값을 받아들일 수 있는 좌측값과 값 역할을 하는 우측값을 구분해 보겠습니다.
int A = 10; // A는 좌측값, 10은 우측값
int B = A; // B는 좌측값, A는 우측값
위의 마지막 문장에서 A는 값의 역할을 합니다.
따라서, 좌측값이자 우측값입니다.
int a, p = 0;
a = p; // a는 좌측값, p는 좌측값이자 우측값
&a = p; // &a는 좌측값이 아님. error!!
&a는 a의 주소를 나타낼 뿐, 값을 받아들일 수 없습니다.
따라서, 우측값입니다.
int a = 0, b = 10, c = 20;
int sum = a + b + c; // sum은 좌측값, a+b+c는 우측값
a+b+c = sum; // error!! a+b+c는 우측값
a, b, c는 모두 값을 받아들일 수 있는 좌측값이지만, a+b+c는 값을 받을 수 없습니다.
따라서, 우측값입니다.
그러면, 다음 코드는 어떨까요?
int x = 10;
int y = 12;
( x < y ? y : x) = 0; // 좌측값
위의 ( x < y ? y : x )는 실제 y를 말하는 것이고, y는 좌측값입니다.
따라서, ( x < y ? y : x ) 표현도 좌측값입니다.
int a = 10;
int* p = &a; // p는 좌측값
*p = 20; // *p는 좌측값
포인터 p는 a의 주소값을 받아들이므로 좌측값입니다.
그리고, 포인터의 역참조 연산자 구문 *p도 값을 받을 수 있으므로 좌측값입니다.
int a = 10; // a는 좌측값
const int b = 20; // b는 좌측값
int& ra = a; // ra는 좌측값
int& rb = 10; // rb는 좌측값이지만, 상수를 참조할 수 없음 error !!
const int& rc = 10; // rc는 좌측값
상수 b도 값을 받으므로 좌측값입니다.
또한, 참조 변수 ra도 좌측값 a를 참조하므로 좌측값입니다.
그리고, 이때의 참조 변수를 좌측값 레퍼런스( l-value reference )라고 부릅니다.
int& rb도 좌측값이지만, 위의 구문은 틀렸습니다.
참조(reference) 변수는 상수를 참조할 수 없기 때문에, 좌측값을 따지기 전에 문장 자체가 성립하지 않습니다.
이와 반대로, const 참조 변수는 상수를 참조할 수 있습니다.
따라서, 상수 10을 참조하는 rc는 좌측값입니다.
함수의 좌측값, 우측값
함수도 좌측값과 우측값이 될 수 있습니다.
int global = 10;
int R_Func(){
return global;
}
int& L_Func(){
return global;
}
int main(){
int A = R_Func(); // R_Func()는 우측값
R_Func() = 20; // R_Func()는 좌측값이 아님. error!!
L_Func() = 20; // L_Func()는 좌측값
return 0;
}
이 코드에서, R_Func()는 값을 받을 수 없습니다.
따라서, R_Func()는 우측값입니다.
하지만, L_Func()는 값을 받을 수 있습니다.
L_Func()는 좌측값입니다. 또한 우측값도 될 수 있습니다.
그런데, 아래의 코드는 어떻게 된 일인지, 오류가 발생하지 않습니다. 왜일까요?
string StrFunc(){
return "test";
}
int main(){
string str = StrFunc(); // StrFunc()는 우측값
StrFunc() = "String-Value"; // StrFunc()는 좌측값이 아님. 오류가 아님
return 0;
}
마지막 문장에서 StrFunc()는 string 임시 객체를 반환하기 때문에, 생성된 임시 객체가 "String-Value" 값을 받습니다.
그래서, 컴파일은 좌측값이 필요하다는 오류를 발생시키지 않은 것입니다.
그러나, 문장이 종료되는 순간, 임시 객체는 사라지므로 좌측값으로 볼 수 없습니다.
정리
- 좌측값은 값을 받아들일 수 있는 표현을 말하며, 우측값은 값을 나타내는 표현입니다.
- 모든 좌측값은 우측값이 될 수 있지만, 모든 우측값이 좌측값은 아닙니다.
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 별명을 만드는 typedef와 using (0) | 2024.07.22 |
---|---|
[C++] 우측값 참조( rvalue reference )와 이동 생성자( move constructor ) (0) | 2024.06.25 |
[C++] friend 키워드 사용법 ( friend 함수, 클래스 ) (0) | 2024.05.30 |
[C++] 접근 지정자 public, protected, private 사용법 (0) | 2024.05.29 |
[C++] 추상 클래스와 순수 가상 소멸자 (0) | 2024.05.25 |