좌측값과 우측값
L-value
이름이 있고 메모리 주소가 있는 값
R-value
일시적인 값, 임시 객체 또는 계산 결과 등
int x = 10; // x는 lvalue, 10은 rvalue
int y = x + 5; // y는 lvalue, (x + 5)는 rvalue
C++ remove_reference_t 함수
remove_reference_t 함수는 레퍼런스를 제거해주는 함수
레퍼런스를 제외한 정확한 타입을 얻기위한 함수
cout << is_same_v<remove_reference_t<int>, int> << "\n"; // true
cout << is_same_v<remove_reference_t<int&>, int> << "\n"; // true
cout << is_same_v<remove_reference_t<int&&>, int> << "\n"; // true
Forwarding Reference
템플릿 함수에서 T&& 형태로 쓰일 때, 좌측값과 우측값 둘다 참조할 수 있다.
- int& : int 타입의 좌측값 참조
- int&& : int 타입의 우측값 참조
- T& : 임의 타입의 좌측값 참조
- T&& : 임의 타입에서 좌측값, 우측값 참조
템플릿이 추론한 타입에 따라 reference collapsing에 의해 좌측값 참조(T&), 우측값 참조(T&&)가 결정된다
- reference collapsing
두 개의 참조 타입중 하나라도 좌측값 참조이면 T&&의 타입은 T&이 된다
TYPE referenceCollapsing(TYPE A, TYPE B){
if( A == LVALUE_REF || B == LVALUE_REF )
return LVALUE_REF;
else
return RVALUE_REF;
}
출처: https://ozt88.tistory.com/46 [공부 모음:티스토리]
예시
template<typename T> void f4(T&& arg) {}
- f4(0)
- T가 int로 추론 됨
- T&& → int&& 가 되어 rvalue 참조가 됨
- 추가적으로
f4<int&&>(0)으로 템플릿이 직적접으로 명시된 경우에만 int&&로 추론
결과적으로 int&& &&가 되어 rvalue 참조가 됨
- f4(n)
- T가 int&로 추론 됨
- T&& → int& &&가 되어 lvalue 참조가 됨
함수에서 사용
void print(string& s) { cout << "lvalue\n"; }
void print(string&& s) { cout << "rvalue\n"; }
template<typename T>
void func(T&& val)
{
print(forward<T>(val)); // perfect forwarding
}
int main()
{
string str = "hi";
func(str); // lvalue → lvalue 유지
func(string("temp")); // rvalue → rvalue 유지
}
- void func(T&& val)
- 템플릿을 통해 변수를 받음
- forward 함수를 통해 좌측값 or 우측값을 완벽 전달
단순히 print(val)로 호출하면 val은 항상 좌측값으로 분류되어
forward 함수로 좌측값 or 우측값으로 캐스팅
- void print(string& s)
void print(string&& s)- 오버로딩을 활용해 좌측값 참조, 우측값 참조 함수를 생성
Forward 함수
Forward 함수는 T&&로 들어온 변수를 좌측값 or 우측값으로 변환하여 완벽하게 전달하기 위해 사용됨
template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept {
return static_cast<_Ty&&>(_Arg);
}
template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept {
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
return static_cast<_Ty&&>(_Arg);
}
좌측값 forward 함수
- constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
- constexpr
컴파일 타임 상수를 리턴타입에 넣어 주면서 constexpr 변수에도 사용가능 - _Ty&&
좌측값 참조 or 우측값 참조가 될 수 있음 - remove_reference_t<_Ty>&
템플릿이 추론한 타입에서 레퍼런스를 제거하여 타입을 선언
ex) remove_reference_t<int&>& → int& 가 됨
이로인해 해당 함수는 좌측값 참조를 매개변수로 갖는 함수가 됨 - noexcept
함수가 예외를 발생하지 않도록 해줌
- constexpr
- return static_cast<_Ty&&>(Arg);
- static_cast<T&&>(Arg)
Arg는 _Ty& 이므로 static_cast< _Ty& &&> → _Ty&로 캐스팅
- static_cast<T&&>(Arg)
우측값 forward 함수
- constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept
- remove_reference_t<_Ty>&&
ex) remove_reference_t<int&&>&& → int&&
이로인해 우측값 참조를 매개변수로 갖음
- remove_reference_t<_Ty>&&
- return static_cast< _Ty &&>(Arg);
- static_cast<t&&>(Arg)
Arg는 _Ty&& 이므로 static_cast<_Ty&& &&> → _Ty&&로 캐스팅
- static_cast<t&&>(Arg)
Move 함수
Move 함수는 주로 좌측값 변수를 우측값으로 캐스팅할 때 사용됨
template <class _Ty>
constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}
- constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept
- remove_reference_t<_Ty>&&
remove_reference_t로 레퍼런스를 제거해 우측값만 return - move(_Ty&& _Arg)
_Ty&&로 좌측값, 우측값 둘다 변수로 받음
- remove_reference_t<_Ty>&&
- return static_cast<remove_reference_t<_Ty>&&>(_Arg);
- (_Arg)
_Arg는 좌측값(&) 또는 우측값(&&) - static_cast<remove_reference_t<_Ty>&&>
remove_reference_t를 사용해 레퍼런스를 제거하여 우측값으로 캐스팅 됨
ex) remove_reference_t<int& or int&&>&& → int&&
- (_Arg)
Move 함수 사용
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> v;
string str = "test";
v.push_back(move(str));
cout << "str: \"" << str << "\"\n";
cout << "v[0]: \"" << v[0] << "\"\n";
}
출력
str: ""
v[0]: "test"
- move 함수를 통해 str을 우측값으로 캐스팅하여 백터에 push_back
- str의 소유권이 v로 이동
- 이때 move는 소유권을 이동시키는게 아닌 우측값 캐스팅만 함
- 소유권 이전은 push_back 함수에서 진행
그래서 move(str)만 실행할 경우 str의 내용물이 남아 있음
추가적으로
noexcept를 붙이는 이유
씹어먹는 C++ - <12 - 1. 우측값 레퍼런스와 이동 생성자>
모두의 코드 씹어먹는 C++ - <12 - 1. 우측값 레퍼런스와 이동 생성자> 작성일 : 2018-03-24 이 글은 77207 번 읽혔습니다. 이번 강좌에서는 복사 생략 (Copy elision)우측값 레퍼런스 (rvalue referen ce)이동 생성
modoocode.com
이동생성자
https://chogyujin-study.tistory.com/42
(C++) 이동생성자(Move Constructor)
이동 생성자(Move Constructor)에 대해 공부하겠습니다. 이동생성자(Move Constructor) 이동 생성자는 객체가 살아있지만 안 쓴다고 보장할 수 있는 상황일 때 사용하게 되는 코드입니다. 기존 객체의 주
chogyujin-study.tistory.com