utility_forward_like
utility_forward_like (std::forward_like)
이 페이지는 C++23에서 도입된 std::forward_like 함수 템플릿을 설명해요. std::forward_like는 주어진 객체의 값 카테고리와 const 한정을 유지하면서 멤버나 원소에 접근할 수 있도록 도와주는 캐스트예요. 주로 "먼 객체(far object)"를 적응시키는 시나리오에서 유용하게 사용돼요.
출처: cppreference
본문
<utility> 헤더에 정의되어 있어요.
Defined in header <utility> |
||
|---|---|---|
template < class T , class U > constexpr auto && forward_like ( U && x ) noexcept ; |
(since C++23) |
T&&와 유사한 속성을 가진 x에 대한 참조를 반환해요.
반환 타입은 다음과 같이 결정돼요:
std::remove_reference_t<T>가 const 한정 타입이라면, 반환 타입의 참조 대상 타입은const std::remove_reference_t<U>예요. 그렇지 않으면 참조 대상 타입은std::remove_reference_t<U>예요.T가 lvalue 참조 타입이라면 반환 타입도 lvalue 참조 타입이에요. 그렇지 않으면 반환 타입은 rvalue 참조 타입이에요.T가 참조 가능한 타입이 아닌 경우(즉, cv 한정void또는 cv 한정자 시퀀스나 ref 한정자가 있는 함수 타입), 프로그램은 ill-formed예요.
매개변수
x |
- | 타입 T처럼 전달되어야 하는 값 |
|---|
반환값
위에서 결정된 타입의 x에 대한 참조예요.
참고 사항
std::forward, std::move, std::as_const와 마찬가지로 std::forward_like는 표현식의 값 카테고리에 영향을 주거나 const 한정을 추가할 수 있는 타입 캐스트예요.
m이 실제 멤버이고 o.m이 유효한 표현식이라면, C++20 코드에서는 보통 std::forward<decltype(o)>(o).m으로 작성해요.
이로 인해 merge, tuple, language라는 세 가지 가능한 모델이 생겨요.
merge: const 한정자를 병합하고 소유자(Owner)의 값 카테고리를 채택해요.tuple:Owner가std::tuple<Member>라고 가정할 때std::get<0>(Owner)가 수행하는 동작이에요.language:std::forward<decltype(Owner)>(o).m이 수행하는 동작이에요.
std::forward_like가 주로 다루는 시나리오는 "먼 객체"를 적응시키는 것이에요. tuple 모델이나 language 모델 모두 이 주요 사용 사례에 적합하지 않기 때문에, std::forward_like에는 merge 모델이 사용돼요.
| Feature-test macro | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_lib_forward_like |
202207L | (C++23) | std::forward_like |
가능한 구현
template < class T , class U > constexpr auto && forward_like ( U && x ) noexcept { constexpr bool is_adding_const = std :: is_const_v < std :: remove_reference_t < T >> ; if constexpr ( std :: is_lvalue_reference_v < T &&> ) { if constexpr ( is_adding_const ) return std :: as_const ( x ); else return static_cast < U &> ( x ); } else { if constexpr ( is_adding_const ) return std :: move ( std :: as_const ( x )); else return std :: move ( x ); } }
예제
#include <cstddef>
#include <iostream>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
struct TypeTeller
{
void operator()(this auto&& self)
{
using SelfType = decltype(self);
using UnrefSelfType = std::remove_reference_t<SelfType>;
if constexpr (std::is_lvalue_reference_v<SelfType>)
{
if constexpr (std::is_const_v<UnrefSelfType>)
std::cout << "const lvalue\n";
else
std::cout << "mutable lvalue\n";
}
else
{
if constexpr (std::is_const_v<UnrefSelfType>)
std::cout << "const rvalue\n";
else
std::cout << "mutable rvalue\n";
}
}
};
struct FarStates
{
std::unique_ptr<TypeTeller> ptr;
std::optional<TypeTeller> opt;
std::vector<TypeTeller> container;
auto&& from_opt(this auto&& self)
{
return std::forward_like<decltype(self)>(self.opt.value());
// It is OK to use std::forward<decltype(self)>(self).opt.value(),
// because std::optional provides suitable accessors.
}
auto&& operator[](this auto&& self, std::size_t i)
{
return std::forward_like<decltype(self)>(self.container.at(i));
// It is not so good to use std::forward<decltype(self)>(self)[i], because
// containers do not provide rvalue subscript access, although they could.
}
auto&& from_ptr(this auto&& self)
{
if (!self.ptr)
throw std::bad_optional_access{};
return std::forward_like<decltype(self)>(*self.ptr);
// It is not good to use *std::forward<decltype(self)>(self).ptr, because
// std::unique_ptr<TypeTeller> always dereferences to a non-const lvalue.
}
};
int main()
{
FarStates my_state
{
.ptr{std::make_unique<TypeTeller>()},
.opt{std::in_place, TypeTeller{}},
.container{std::vector<TypeTeller>(1)},
};
my_state.from_ptr()();
my_state.from_opt()();
my_state[0]();
std::cout << '\n';
std::as_const(my_state).from_ptr()();
std::as_const(my_state).from_opt()();
std::as_const(my_state)[0]();
std::cout << '\n';
std::move(my_state).from_ptr()();
std::move(my_state).from_opt()();
std::move(my_state)[0]();
std::cout << '\n';
std::move(std::as_const(my_state)).from_ptr()();
std::move(std::as_const(my_state)).from_opt()();
std::move(std::as_const(my_state))[0]();
std::cout << '\n';
}
출력:
mutable lvalue
mutable lvalue
mutable lvalue
const lvalue
const lvalue
const lvalue
mutable rvalue
mutable rvalue
mutable rvalue
const rvalue
const rvalue
const rvalue
같이 보기
move (C++11) |
인수를 xvalue로 변환해요 (함수 템플릿) [edit] |
|---|---|
forward (C++11) |
함수 인수를 전달하고 타입 템플릿 인수를 사용해 값 카테고리를 보존해요 (함수 템플릿) [edit] |
as_const (C++17) |
인수에 대한 const 참조를 얻어요 (함수 템플릿) [edit] |