Espacios de nombres
Variantes

std::forward_like

De cppreference.com
 
 
Biblioteca de servicios
 
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:

  1. Si std::remove_reference_t<T> es un tipo con calificador 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>.
  2. Si T es 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 de Owner.
  • tupla: lo que hace std::get<0>(Owner), suponiendo que Owner es un std::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) [editar]
(C++11)
Reenvía una función argumento
(plantilla de función) [editar]
(C++17)
Obtiene una referencia a const para su argumento.
(plantilla de función) [editar]