std::forward_like
| Definido en el archivo de 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 con calificadorconst, entonces el tipo referenciado del tipo de retorno esconst std::remove_reference_t<U>. De lo contrario, el tipo referenciado esstd::remove_reference_t<U>. - Si
Tes un tipo referencia a l-valor, entonces el tipo de retorno también es un tipo referencia a l-valor. De lo contrario, el tipo de retorno es un tipo referencia a r-valor.
Si T no es un tipo referenciable (es decir, posiblemente void con calificador const volatile o un tipo función con una sec-calificador-cv o un calificador-ref), el programa es malformado.
Parámetros
| x | - | Un valor que necesita ser reenviado como el 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 un tipo de conversión que solo influye en la categoría de valor de una expresión, o potencialmente agrega calificación const.
Cuando m es un miembro concreto y, por lo tanto, o.m es una expresión válida, esto generalmente se escribe como std::forward<decltype(o)>(o).m en el código de C++20.
Esto conduce a tres modelos posibles, llamados fusión, tupla, y lenguaje.
- fusión: combina los calificadores
const, y adopta la categoría de valor deOwner. - tupla: lo que hace
std::get<0>(Owner), suponiendo queOwneres unstd::tuple<Member>. - lenguaje: lo que hace
std::forward<decltype(Owner)>(o).m.
El escenario principal al que std::forward_like se adapta es la adaptación de objetos “lejanos”. Ni los escenarios tupla ni lenguaje hacen lo correcto para ese caso de uso principal, por lo que se utiliza el modelo fusión para std::forward_like.
| Macro de Prueba de característica | Valor | Estándar | Comentario |
|---|---|---|---|
__cpp_lib_forward_like |
202207L |
(C++23) | std::forward_like
|
Posible implementación
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 << "l-valor const\n";
else
std::cout << "l-valor mutable\n";
}
else
{
if constexpr (std::is_const_v<UnrefSelfType>)
std::cout << "r-valor const\n";
else
std::cout << "r-valor mutable\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());
// Es correcto usar 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));
// No es tan bueno usar std::forward<decltype(self)>(self)[i], porque
// los contenedores no proporcionan acceso a subíndices r-valor, 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 tan bueno usar *std::forward<decltype(self)>(self).ptr, porque
// std::unique_ptr<TypeTeller> siempre se desreferencia a un l-valor no-const.
}
};
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';
}
Salida:
l-valor mutable
l-valor mutable
l-valor mutable
l-valor const
l-valor const
l-valor const
r-valor mutable
r-valor mutable
r-valor mutable
r-valor const
r-valor const
r-valor const
Véase también
(C++11) |
Obtiene una referencia r-valor (plantilla de función) |
(C++11) |
Reenvía una función argumento (plantilla de función) |
(C++17) |
Obtiene una referencia a const para su argumento. (plantilla de función) |