M.2 R-value references
챕터 1에서 우리는 l-value와 r-value에 대해서 이야기 했었다
그리고 이에 대해서 걱정할 것이 없다고 말했다
c++11 전까지는 나름 유효한 조언이었으나 c++11의 move semantics를 이해하기 위해서
이 주제에 대해서 재조명해야 할 필요가 있다
L-values and r-values
"value"라는 이름에도 불구하고 이들은 value에 특성이 아닌 expression의 특성에 대해서 이야기 하고 있다.
c++의 모든 expression은 두가지 특성을 가지고 있다
하나는 타입 (타입 checking에 쓰이는)
다른 하나는 value category (expression의 결과를 다음에 할당할 수 있는지 여부와 같은 특정 종류의 구문 검사에 사용된다)
c++03 이전에는 l-value와 r-value 두가지 value cateogyr만 이용 가능했다
정확히 어떤 expression이 l-value이고 r-value인지는 꽤나 목잡하다.
따라서 간단하게 우리의 목적을 만족하는 범위내에서 살펴본다
l-value는 function 혹은 object로 생각하는 것이 가장 간단하다. 모든 l-value는 assigned memory를 address를 가지고 있다
원래 정의되기론 l-value는 assignment expression의 left-hand side에 적합한 value라고 했지만 const 키워드가 추가되고나서 l-value는 두 가지 sub-categories로 분리되었다
modifiable l-value, 그리고 non-modifiable l-value(const)
r-value는 간단하게 l-value가 아닌 모든 것이라고 생각하면 된다.
예를들어 literal (e.g. 5), temporary values (e.g. x+1), 그리고 anonymous object (e.g. Class(5,2))와 같은 것들이 있다. r-value는 일반적으로 그들의 value를 평가받고 expression scope를 지니고 있고 (scope를 벗어나면 die), 값을 할당받지 못한다.
Move semantics를 지원하기 위해 c++11에서는 3가지 새로운 카테고리를 소개했다
pr-values, x-values, and gl-values. 일단 우리는 이를 생략한다
L-value reference
c++11 이전에는 오직 "reference"라는 타입의 reference만 존재했다
그러나 c++11에서는 l-value reference라고 불리운다
L-value reference는 오직 modifiable l-value로만 initialized가 가능하다
L-value reference to const object는 l-value와 r-value로 initialized 가능하다
그러나 이 value는 수정이 불가능하다
const object에 대한 L-vlaue reference는 argument의 copy를 만들지 않고도 모든 유형의 argument(l-value 또는 r-value)를 함수에 전달할 수 있기 때문에 특히 유용합니다.
R-value references
c++11에서는 새로운 타입의 reference가 추가되었다. 이는 r-value reference라고 불리운다
r-value reference는 오직 r-value롤만 initialized 될 수 있도록 설계되었다
l-value reference가 single ampersand를 사용하는 반면에
r-value reference는 double ampersand로 생성된다
int x{ 5 };
int &lref{ x }; // l-value reference initialized with l-value x
int &&rref{ 5 }; // r-value reference initialized with r-value 5
R-value reference는 l-value로 initialized될 수 없다
R-value reference는 두가지 유용한 특성을 가지고 있다
첫째, rv ref는 initialized에 사용한 object의 수명을 r-value ref의 수명으로 연장시킨다
둘째, non-const r-value reference는 r-value를 수정할 수 있게 허용해준다
예시를 살펴보자
#include <iostream>
class Fraction
{
private:
int m_numerator;
int m_denominator;
public:
Fraction(int numerator = 0, int denominator = 1) :
m_numerator{ numerator }, m_denominator{ denominator }
{
}
friend std::ostream& operator<<(std::ostream& out, const Fraction &f1)
{
out << f1.m_numerator << '/' << f1.m_denominator;
return out;
}
};
int main()
{
auto &&rref{ Fraction{ 3, 5 } }; // r-value reference to temporary Fraction
// f1 of operator<< binds to the temporary, no copies are created.
std::cout << rref << '\n';
return 0;
} // rref (and the temporary Fraction) goes out of scope here
위 프로그램의 출력은 다음과 같다
3/5
anonymous object이므로 일반적으로 expression이 끝나면 scope를 벗어나게 된다
그러나 우리가 r-value ref로 initialize 했기 때문에 block이 끝나기 전까지 수명이 연장됐다
그리고 우리는 이를 print에 사용할 수 있다
다음 예시를 살펴보자
#include <iostream>
int main()
{
int &&rref{ 5 }; // because we're initializing an r-value reference with a literal, a temporary with value 5 is created here
rref = 10;
std::cout << rref << '\n';
return 0;
}
위 프로그램의 출력은 다음과 같다
10
literal vlaue를 수정하는게 이상해보일 수는 있지만
위와 같이 r-value ref는 literal로 initialize 됐음에도 불구하고 수정이 가능하다
R-value ref는 앞선 사례와 같이 잘 사용되지는 않는다
R-value references as function parameters
R-value ref는 function parameter로 자주 사용된다
이는 l-value, r-value에 각기 다른 function을 overload할 때 유용하다
void fun(const int &lref) // l-value arguments will select this function
{
std::cout << "l-value reference to const\n";
}
void fun(int &&rref) // r-value arguments will select this function
{
std::cout << "r-value reference\n";
}
int main()
{
int x{ 5 };
fun(x); // l-value argument calls l-value version of function
fun(5); // r-value argument calls r-value version of function
return 0;
}
이는 다음과 같은 출력을 준다
l-value reference to const
r-value reference
보시다시피 l-value를 pass하면 overload function을 l-value ref로 이를 매칭하고
r-value의 경우 r-value ref로 매칭한다
언제 우리는 이렇게 되기를 원할까? 자세한 내용은 다음 레슨에서 살펴보자
이는 말할 필요도 없이 move semantics에서 중요한 부분이다
한가지 흥미로운 예시가 있다
int &&ref{ 5 };
fun(ref);
이렇게 되면 ref는 r-value ref지만 l-value버젼의 fun function을 호출한다
비록 r-value ref지만 ref 스스로는 l-value이기 때문이다
이렇게 보면 혼동스럽다 따라서 다음과 같이 이해해보자
Named-object는 l-value이고 Anonymous object는 r-value이다
type은 object가 r-value인지 l-value인지 결정하지 않는다
Returning an r-value reference
우리가 l-value ref로 return 값을 설정하지 않았듯이
r-value ref로도 우리는 return 값을 설정하지 않는다
r-value ref도 마찬가지로 scope를 벗어나는 순간 수명이 끝나기 때문이다
Author And Source
이 문제에 관하여(M.2 R-value references), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@ikmy0ung/M.2-R-value-references저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)