r-value 참조
r-value는 단순 대입 연산자의 오른쪽 항을 말하며
r-value로써 변수가 올 수도 있고 1, 5와 같은 리터널 상수가 올 수도 있습니다.
Ex. int a = 3; -> 3은 r-value
1에다가 5를 참조할 수 없듯이 변수가 아닌 대상(l-value)에 참조를 선언하는 건 허용되지 않았으나
C++11에 r-value에 대한 참조자가 나오면서 가능하게 되었습니다.
r-value 참조자의 문법은 아래와 같습니다.
1
2
// r-value 참조는 && 가 붙는다.
int&& a = 3;
언제 사용하는데?
아래 코드를 보면 묵시적 형변환으로 인해 int n이 Temp 클래스 임시 객체로 변환되는 것을 볼 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<iostream>
class Temp
{
private:
int num = 0;
public:
Temp() = default;
// 파라미터가 1개인 생성자는 묵시적으로 형변환이 가능하다.
Temp(int rhs) :num(rhs)
{
std::cout << "Enter: Temp(int rhs)" << std::endl;
}
int GetNum() const { return num; }
};
void TEST(Temp t)
{
std::cout << "Enter: TEST(TEMP t)" << std::endl;
std::cout <<"num: " << t.GetNum() << std::endl;
}
int main()
{
TEST(1 + 4);
}
// 출력 결과
Enter: Temp(int rhs)
Enter: TEST(TEMP t)
num: 5
위 코드에서 TEST(Temp t) 함수를 보면
우리가 보통 C++에서 클래스를 파라미터로 넘길 때 참조 형식으로 보내지만 값 형식으로 되어 있는 것을 볼 수 있습니다.
그러므로 코드를 아래와 같이 변경해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
// 클래스 생략
void TEST(Temp& t)
{
std::cout << "Enter: TEST(TEMP& t)" << std::endl;
std::cout <<"num: " << t.GetNum() << std::endl;
}
int main()
{
TEST(1 + 4); // compile error - 비const 참조에 대한 초기 값은 lvalue여야 합니다.
}
TEST() 함수의 파라미터를 Temp& t로 변경하니 컴파일 에러가 발생했습니다.
이때 const Temp& t로 변경하면 문제가 해결되지만 const가 되면 값 수정이 불가하게 되기 때문에 값 수정이 필요할 함수라면 문제 해결이 애매해지는데요.
이때 r-value 참조를 통해 해결할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void TEST(Temp& t)
{
std::cout << "Enter: TEST(TEMP& t)" << std::endl;
std::cout << "num: " << t.GetNum() << std::endl;
}
void TEST(Temp&& t)
{
std::cout << "Enter: TEST(TEMP&& t)" << std::endl;
std::cout <<"num: " << t.GetNum() << std::endl;
}
int main()
{
TEST(1 + 4);
}
// 출력 결과
Enter: Temp(int rhs)
Enter: TEST(TEMP&& t)
num: 5
내용을 정리하면 아래와 같습니다.
- 1+4은 r-value 연산에 대한 임시 객체이기 때문에 참조 형식 l-value에 할당할 수 없다
1
int& a = 4; // compile error - 비const 참조에 대한 초기 값은 lvalue여야 합니다.
- 그러므로 r-value 연산에 대한 임시 객체인 (1+4)를 받기 위해선 r-value 참조를 사용해야 한다.
1
int&& a = 4; // it's ok.
함수의 모호성
위와 같이 r-value를 사용하기 위해선 아래와 같은 함수의 모호성에 대해 조심해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void TEST(Temp t)
{
std::cout << "Enter: TEST(TEMP t)" << std::endl;
std::cout << "num: " << t.GetNum() << std::endl;
}
void TEST(Temp&& t)
{
std::cout << "Enter: TEST(TEMP&& t)" << std::endl;
std::cout <<"num: " << t.GetNum() << std::endl;
}
int main()
{
// Compile Error! - 오버로드된 함수 "TEST"의 인스턴스 중 두 개 이상이 인수 목록과 일치합니다.
TEST(1 + 4);
}