move와 forward 분석
1. move
template <class _Ty>
constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}
1-1. 인자값
move(_Ty&& _Arg) noexcept
들어오는 input값을 T&&
Universal Reference로 받고 있다
- T > T&
- T& > T&
- T&& > T&& 즉 lvalue든 rvalue든 상관없이 둘 다 받겠다는 뜻
1-2. 함수내부
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
remove_reference_t?
들어온 타입의 reference 형을 지워주는 역할
type이 lvalue든 rvalue든 reference를 지워서 일반자료형으로 만든 다음에 rvalue(&&) 형으로 static_cast를 시키므로 무조건 rvalue로서의 캐스팅 역할을 수행하는 것
1-3. 반환 타입
constexpr remove_reference_t<_Ty>&&
constexpr로 컴파일타임 때 동작하도록 함수를 상수화 위와 같이 reference를 지우고 &&를 추가하면 무조건 rvalue가 됨
2. forward
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);
}
2-1. 인자값
forward(remove_reference_t<_Ty>& _Arg) noexcept
type에 존재하는 reference를 지워버리고 추가로 reference 연산으로 받는다 즉, _arg가 lvalue일 경우에 받는다
forward(remove_reference_t<_Ty>&& _Arg) noexcept
이 부분도 위와 같이 기존 존재하는 reference를 지우고 추가로 이동연산으로 받는다 즉, _arg가 rvalue일 경우에 받는다
이와 같이 lvalue와 rvalue를 나눠서 받는 이유는 다음 처리를 하기 위해서다
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
static_assert : 컴파일 타임 assert. 조건식이 false면 에러를 발생시킨다
is_lvalue_reference_v<type> : 들어온 type이 lvalue면 true를 반환해주는 type-trait
즉, 이 부분은 들어온 type이 lvalue라면 에러를 발생시키겠다는 의미이다.
그럼 여기서 궁금증이 생기는데
forward(remove_reference_t<_Ty>&& _Arg) noexcept {
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
들어온 인자값이 rvalue인데 해당 타입이 lvalue일 경우가 있을까? 다음과 같은 경우가 있다.
std::forward<std::string&>(std::string{});
해당 예시를 보면 들어오는 값 자체는 rvalue(string{}
)로 들어오지만
타입은 lvalue(string&
)로 들어왔기 때문에 에러를 발생시킨다
2-2. 함수내부
return static_cast<_Ty&&>(_Arg);
들어오는 타입을 _Ty&&
형태로 캐스팅 해주는데 이게 단순이 rvalue로서의 캐스팅이 아니다
Reference Collapsing
기존 타입의 Reference와 Reference casting이 합쳐지면 다음과 같이 작동한다
일반 자료형의 경우 casting을 하면 &, && 로 무조건 casting된다
하지만 & 또는 && 를 만나게 되면 Reference Collapsing 법칙에 따라 동작한다
단순하게 생각하면 _Ty&&
가 되는 경우는 &&와 &&가 만났을 때 뿐이고
&를 1, && 를 0이라고 한 뒤 두 개를 or 연산한다고 생각하면 편하다
따라서 이 부분은 결국
return static_cast<_Ty&&>(_Arg);
기존 타입(_Ty
)이 rvalue면 rvalue로서의 casting을 수행하고 lvalue면 lvalue로서의 casting을 수행하기 때문에
기존에 알고 있던 forward의 기능이 잘 동작한다
2-3. 반환 타입
constexpr _Ty&&
반환 타입 역시 Universal Reference로 받고 있기 때문에 lvalue -> lvalue rvalue -> rvalue 로서 반환타입을 잘 표시한다
댓글남기기