인프런 커뮤니티 질문&답변

Gretel님의 프로필 이미지
Gretel

작성한 질문수

홍정모의 따라하며 배우는 C++

6.15 참조와 const

const int&를 사용했을때 왜 리터럴값이 들어가도 작동하는지

작성

·

648

0

const int&를 사용했을때 왜 리터럴값이 들어가도 작동하는지에 대한 원리를 알 수 있을까요?

뒷쪽에서 익명객체 파트 듣다가 궁금한게 생겼는데

class mine {

private:

int value;

public:

mine(int m) :value(m) {}

int getvalue() const {

return value;

}

};

mine add(mine &m1, mine &m2) {

return mine(m1.getvalue() + m2.getvalue());

}

int main() {

cout << add(mine(5), mine(6)); 

return 0;

}

이 코드에서 add 함수 파라미터에 const를 붙이지 않으면 main에서 add함수가 작동하지 않습니다. 

 따로 mine m1(5) ,m2(6) ; 등을 만들어 add(m1,m2)를 하면 작동이 됩니다.

왜 이런일이 일어나는지에 대해 조금 혼란이 생겨서 앞 강의들을 다시 들어보다 여기까지 왔습니다. 답변 부탁드립니다

답변 2

17

안녕하세요!

const 레퍼런스는 L-value, R-value 모두를 참조 할 수 있으며 그냥 일반 레퍼런스는 L-value만 참조할 수 있기 때문입니다. 에러가 나신 이유는 일반 레퍼런스에 R-value를 참조하셨기 때문입니다.

혹시 L-value와 R-value 개념이 생소하실까봐 설명 드리자면 (알고 계시다면 스킵해주세요!)

L-value는 데이터를 저장할 수 있는 유의미한 메모리 공간을 의미합니다. 따라서 L-value들은 개발자가 포인터로 접근할 수 있는 "유효한 주소"를 가집니다. int a = 2; 여기서 a는 L-value입니다. 2라는 값은 a 에 저장할 수 있습니다. a는 유의미한, 데이터를 저장할 수 있는 공간인 "변수"이기 때문입니다. 따라서 L-value들은 `=` 대입 연산자에 있어서 주로 할당받는 역할을 하기 때문에 왼쪽에 오게 됩니다. int b = 2; int a = b; 이런 경우처럼 오른쪽에도 올 수 있습니다. b라는 공간 안에 있는 값을 복사하여 a라는 공간에 할당하는 의미인데 이런 경우엔 L-value도 오른쪽에 올 수 있네요. L-value는 직접 데이터를 저장할 수 있는 이름이 있는 공간이라고 생각해주시면 될 것 같아요.

R-value는 데이터를 저장할 수 없는, 시스템상 잠깐 동안만 쓰이고 버려지는 임시 공간입니다.  int a = 3; 이 표현식에서 3이 바로 R-value가 됩니다. 3 은 변수가 아니죠. 3 은 어떤 값을 집어넣어 저장할 수 있는 유의미한 공간이 아닙니다. 3이라는 것은 a에 3이라는 값을 넣어주기 위해서 아주 잠깐동안만 임시로 존재하다가 이내 사라지는 그런 이름 없는 존재입니다. 이런게 바로 R-value입니다. (함수로부터 리턴받은 값도 리턴 후 사라지는 R-value입니다.) 따라서 R-value는 절대 '=' 대입 연산자에서 왼쪽에 올 수 없습니다. 값을 할당할 수 있는 공간이 아니기 때문이죠!  3 = a; 3 = 2 이런건 있을 수 없는 표현이죠. 이런 "Hello", 323 이런 리터럴들은 전부 R-value라고 생각하시면 됩니다. 이렇게 R-value는 이름이 없는 공간, 개발자가 임의로 무언갈 저장할 수 없는 공간이며 동시에 임시적으로 잠깐 쓰이고 버려지는 공간이라고 생각해주시면 됩니다. 따라서 리터럴 뿐만 아니라 함수의 실행 결과로 리턴받은 데이터나 이름 없는 '익명 객체' 또한 R-value가 됩니다.

일반 레퍼런스는 오직 L-value만 참조할 수 있습니다. 따라서 add 함수의 매개변수는 mine & m1, mine & m2 이런 일반 레퍼런스이기에 R-value 를 파라미터로 넘길 수 없습니다. add(mine(5), mine(6)); 파라미터로  mine(5)와 mine(6)을 넘기셨는데 이것들은 R-value입니다. 이름이 없는 익명객체이기 때문입니다. (익명 객체는 8.13 강의에서 자세히 배우시게 됩니다.)

mine m1(5) ,m2(6) ; 이렇게 m1과 m2이라는 이름으로 접근할 수 있게 됐기 때문에 여기서 m1과 m2는 L-value입니다. m1과 m2라는 이름으로 접근할 수 있으며 이 메모리의 값을 수정하고 다른 값을 집어넣어 저장할 수도 있어요. 객체의 이름이 m1과 m2인 것이죠! add(m1,m2) 는 L-value를 넘기셨던것이니 작동이 잘 되었던 것입니다.

그러나 그냥 mine(5) 와 mine(6) 은 위와 같은 m1, m2 이런 이름 없이 생성된 객체나 마찬가지입니다. 이렇게 익명으로 객체를 생성하시면 이 객체는 어떤 이름을 가지는 메모리가 아니기에 개발자가 임의로 다른 값을 집어넣거나 할 수 있는 유의미한 공간이 아니게 됩니다. 따라서 add(mine(5), mine(6)); 이렇게 파라미터를 넘기시면 R-value 를 넘긴 것이니 일반 레퍼런스인 mine& m1과 mine& m2이 이를 참조할 수 없어 컴파일 에러가 발생하셨던 것입니다. 마치  int & a = 2 이렇게 쓰신것과 마찬가지에요.

const 레퍼런스는 L-value와 R-value 둘 다 참조할 수 있기에 const를 붙이면 add(mine(5), mine(6)); 이렇게 익명 객체(R-value)를 파라미터로 넘겨도 잘 작동했던 것입니다. 

일반 레퍼런스가 R-value를 참조할 수 없는 이유는, 직관적으로 생각해보시면 int & a = 2 이게 가능한 표현식이 된다고 가정해보면 a는 2의 별명이 되므로 a = 5 이 식은 즉 2 = 5 와도 같아지고 이게 가능해진다는 것인데.. 이건 말이 되지 않죠!  2는 어떤 값을 저장할 수 있는 유의미한 이름있는 공간이 아니기 떄문입니다. 따라서 일반 레퍼런스는 R-value를 참조할 수 없도록 문법적으로 막혀있는 것입니다.

반면 const레퍼런스가 R-value 또한 참조할 수 있는 이유는, const int & a = 2; 라는 표현식은 a가 2라는 임시 공간의 별명이 된다는 것인데 const가 붙었으므로 이 2라는 공간을 수정하거나 어떤 값을 넣어주려는 행위는 어차피 불가능하죠! const가 붙었기때문에 어차피 a = 3 이런 수정이 불가능하기 때문에 가능한 것입니다. 이렇게 const레퍼런스가 R-value를 참조하게 되면 수정은 불가하고 오직 "접근" 만 가능하도록 보장해주는 것이니 문제없습니다. 이 2라는 임시 공간을 set은 못하고 get만 할 수 있도록 보장되니 가능한 것이에요. 

도움이 되셨으면 좋겠습니다. 

감사합니다

0

Gretel님의 프로필 이미지
Gretel
질문자

자세한 답변 너무 감사드립니다. r밸류와 l밸류를 제대로 이해했고 그에 따른 const 의 역할 또한 한번 더 짚게됐습니다. 감사합니다!!!

Gretel님의 프로필 이미지
Gretel

작성한 질문수

질문하기