LValue and RValue References (98, 03, 11)
개요
C++에서 모든 표현식(expression)은 "LValue" 또는 "RValue"라는 두 가지 기본 속성 중 하나를 가집니다. 이 둘을 구분하는 것은 함수 오버로딩, 이동 의미론(move semantics), 완벽한 전달(perfect forwarding)과 같은 C++의 고급 기능을 이해하고 효율적인 코드를 작성하는 데 매우 중요합니다.
- LValue (Locator Value): 메모리 상의 위치를 가지며, 이름으로 참조할 수 있는 객체입니다. 등호(=)의 왼쪽에 올 수 있습니다. (예: 변수)
- RValue (Right Value): 임시적인 값으로, 메모리 상의 특정 위치를 가지고 있지 않습니다. 등호(=)의 오른쪽에만 올 수 있습니다. (예: 리터럴, 함수 반환 값)
C++ 버전별 주요 키워드 도입 시기
- C++98/03: LValue 참조 (
&
)가 도입되었습니다. - C++11: RValue 참조 (
&&
),std::move
,std::forward
가 도입되어 이동 의미론과 완벽한 전달이 가능해졌습니다.
내용 설명
1. 값에 의한 전달 (Pass by Value)
void func(int v)
- 값을 복사하여 새로운 객체를 생성합니다.
- 원본 데이터는 절대 수정되지 않습니다.
- LValue, RValue 모든 타입의 인자를 받을 수 있습니다.
2. 비상수 LValue 참조 (Non-const LValue Reference)
void func(int& v)
- 객체의 별칭(alias)으로 동작하며, 복사가 발생하지 않습니다.
- 참조를 통해 원본 객체를 수정할 수 있습니다.
- 수정 가능한 LValue만 인자로 받을 수 있습니다. (const LValue나 RValue는 전달 불가)
3. 상수 LValue 참조 (const LValue Reference)
void func(const int& v)
- 비상수 LValue 참조처럼 복사 없이 객체를 참조하지만, 참조를 통해 값을 수정할 수 없습니다.
- LValue, RValue 모든 타입의 인자를 받을 수 있습니다.
- RValue를 인자로 받으면, 임시 객체의 생명주기가 참조가 유효한 동안으로 연장됩니다.
4. RValue 참조 (RValue Reference)
void func(int&& v)
- C++11에서 도입되었으며, 소멸될 예정인 임시 객체(RValue)를 참조합니다.
- 이동 의미론(Move Semantics)을 구현하는 데 사용되며, 불필요한 복사를 피하고 리소스 소유권을 효율적으로 이전할 수 있습니다.
예제 코드
#include <iostream>
// 값에 의한 전달 (Pass by Value)
void copy_by_value(int v) // 입력 전용
{
std::cout << "copy_by_value: " << v << std::endl;
}
// 상수 값에 의한 전달 (Pass by const Value)
void copy_by_value_const(const int v) // 입력 전용
{
std::cout << "copy_by_value_const: " << v << std::endl;
}
// 비상수 참조 (Non-const Reference)
void reference(int &v) // 입출력 가능
{
std::cout << "reference: " << v << std::endl;
v = v * 2; // 원본 값 수정 가능
}
// 상수 참조 (const Reference)
void const_reference(const int &v) // 입력 전용
{
std::cout << "const_reference: " << v << std::endl;
// v = 10; // 컴파일 오류: 상수 참조는 수정 불가
}
int main()
{
int lvalue = 2;
const int lvalue_const = 2;
std::cout << "--- 값에 의한 전달 ---" << std::endl;
copy_by_value(lvalue); // OK: LValue 전달
copy_by_value(lvalue_const); // OK: const LValue 전달
copy_by_value(2); // OK: RValue 전달
std::cout << "\n--- 상수 값에 의한 전달 ---" << std::endl;
copy_by_value_const(lvalue); // OK: LValue 전달
copy_by_value_const(lvalue_const); // OK: const LValue 전달
copy_by_value_const(2); // OK: RValue 전달
std::cout << "\n--- 비상수 참조 ---" << std::endl;
reference(lvalue); // OK: LValue 전달. lvalue는 이제 4가 됨
// reference(lvalue_const); // 오류: const LValue는 비상수 참조로 전달 불가
// reference(2); // 오류: RValue는 비상수 참조로 전달 불가
std::cout << "lvalue after reference call: " << lvalue << std::endl;
std::cout << "\n--- 상수 참조 ---" << std::endl;
const_reference(lvalue); // OK: LValue 전달 (현재 값 4)
const_reference(lvalue_const); // OK: const LValue 전달
const_reference(2); // OK: RValue 전달 (임시 객체 생성)
return 0;
}
실행 결과
--- 값에 의한 전달 ---
copy_by_value: 2
copy_by_value: 2
copy_by_value: 2
--- 상수 값에 의한 전달 ---
copy_by_value_const: 2
copy_by_value_const: 2
copy_by_value_const: 2
--- 비상수 참조 ---
reference: 2
lvalue after reference call: 4
--- 상수 참조 ---
const_reference: 4
const_reference: 2
const_reference: 2
활용팁
- 이동 의미론 (Move Semantics):
std::move
를 사용하여 LValue를 RValue처럼 취급하게 만들 수 있습니다. 이를 통해 임시 객체가 아닌 객체의 리소스(예: 동적 할당된 메모리)를 복사 없이 '이동'하여 성능을 향상시킬 수 있습니다.std::vector
,std::string
,std::unique_ptr
와 같은 표준 라이브러리 컨테이너에서 널리 사용됩니다. - 완벽한 전달 (Perfect Forwarding):
std::forward
는 템플릿 함수에서 인자의 값 카테고리(LValue 또는 RValue)를 그대로 유지하면서 다른 함수로 전달할 때 사용됩니다. 이를 통해 불필요한 복사를 방지하고, 팩토리 함수나 래퍼 함수를 효율적으로 작성할 수 있습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : dynamic heap memory allocation (98, 11, 14) (1) | 2025.08.14 |
---|---|
Modern C++ : pointer and address (98, 11) (2) | 2025.08.13 |
Modern C++ : const reference and value semantics (98, 11) (1) | 2025.08.12 |
Modern C++ : string vs std::string vs std::array<char, N> (98, 11, 17) (1) | 2025.08.11 |
Modern C++ : array vs std::array (98, 11) (1) | 2025.08.10 |
Modern C++ : anonymous namespace (98, 11, 17) (2) | 2025.08.09 |
Modern C++ : C-Style static function (98, 11) (1) | 2025.08.08 |
Modern C++ : static local variables (98, 11) (3) | 2025.08.07 |