C, C++/C++ 언어 / / 2024. 6. 23.

[C++] 좌측값( l-value )과 우측값( r-value ) 구분하기

 

좌측값( 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" 값을 받습니다.

그래서, 컴파일은 좌측값이 필요하다는 오류를 발생시키지 않은 것입니다.

그러나, 문장이 종료되는 순간, 임시 객체는 사라지므로 좌측값으로 볼 수 없습니다.

 

정리

  • 좌측값은 값을 받아들일 수 있는 표현을 말하며, 우측값은 값을 나타내는 표현입니다.
  • 모든 좌측값은 우측값이 될 수 있지만, 모든 우측값이 좌측값은 아닙니다.

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유