std:: forward_like
|
Definido en el encabezado
<utility>
|
||
|
template
<
class
T,
class
U
>
constexpr auto && forward_like ( U && x ) noexcept ; |
(desde C++23) | |
Devuelve una referencia a
x
que tiene propiedades similares a
T&&
.
El tipo de retorno se determina de la siguiente manera:
- Si std:: remove_reference_t < T > es un tipo calificado como const, entonces el tipo referenciado del tipo de retorno es const std:: remove_reference_t < U > . De lo contrario, el tipo referenciado es std:: remove_reference_t < U > .
-
Si
T&&es un tipo de referencia a lvalue, entonces el tipo de retorno también es un tipo de referencia a lvalue. De lo contrario, el tipo de retorno es un tipo de referencia a rvalue.
Si
T
no es un
tipo referenciable
, el programa está mal formado.
Contenidos |
Parámetros
| x | - |
un valor necesita ser reenviado como tipo
T
|
Valor de retorno
Una referencia a x del tipo determinado como se indicó anteriormente.
Notas
Al igual que
std::forward
,
std::move
, y
std::as_const
,
std::forward_like
es una conversión de tipo que solo influye en la
categoría de valor
de una expresión, o potencialmente añade calificación const.
Cuando
m
es un miembro real y por lo tanto
o.
m
una expresión válida, esto generalmente se escribe como
std::
forward
<
decltype
(
o
)
>
(
o
)
.
m
en código C++20.
Esto conduce a tres modelos posibles, llamados merge , tuple , y language .
-
merge
: combinar los
const
qualifiers, y adoptar la categoría de valor del
Owner. -
tuple
: lo que
std
::
get
<
0
>
(
Owner
)
hace, asumiendo que
Owneres un std:: tuple < Member > . - language : lo que std:: forward < decltype ( Owner ) > ( o ) . m hace.
El escenario principal para el que
std::forward_like
está diseñado es adaptar objetos "lejanos". Ni el
tuple
ni el
language
hacen lo correcto para ese caso de uso principal, por lo que se utiliza el modelo de
merge
para
std::forward_like
.
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_lib_forward_like
|
202207L
|
(C++23) |
std::forward_like
|
Implementación posible
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); } } |
Ejemplo
#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(), // porque std::optional proporciona accesores adecuados. } 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 // los contenedores no proporcionan acceso de subíndice con valor r, aunque podrían. } auto&& from_ptr(this auto&& self) { if (!self.ptr) throw std::bad_optional_access{}; return std::forward_like<decltype(self)>(*self.ptr); // No es bueno usar *std::forward<decltype(self)>(self).ptr, porque // std::unique_ptr<TypeTeller> siempre se desreferencia a un lvalue no constante. } }; 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::mover(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::mover(std::as_const(my_state)).from_opt()(); std::move(std::as_const(my_state))[0](); std::cout << '\n'; }
Salida:
mutable lvalue mutable lvalue mutable lvalue const lvalue const lvalue const lvalue mutable rvalue mutable rvalue mutable rvalue const rvalue const rvalue const rvalue
Véase también
|
(C++11)
|
convierte el argumento a un xvalue
(function template) |
|
(C++11)
|
reenvía un argumento de función y utiliza el argumento de plantilla de tipo para preservar su categoría de valor
(function template) |
|
(C++17)
|
obtiene una referencia
const
a su argumento
(function template) |