Namespaces
Variants

std::forward_like

De es.cppreference.net
 
 
Biblioteca de utilidades
Soporte del lenguaje
Soporte de tipos (tipos básicos, RTTI)
Macros de prueba de características de biblioteca (C++20)
Utilidades de programa
Funciones variádicas
Soporte de corrutinas (C++20)
Soporte de contratos (C++26)
Comparación de tres vías
(C++20)
(C++20)(C++20)(C++20)    
(C++20)(C++20)(C++20)

Utilidades generales
Operadores relacionales (obsoleto en C++20)
 
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:

  1. Si std::remove_reference_t<T> es un tipo calificado como const, entonces el tipo referido del tipo de retorno es const std::remove_reference_t<U>. De lo contrario, el tipo referido es std::remove_reference_t<U>.
  2. Si T es un tipo de referencia lvalue, entonces el tipo de retorno también es un tipo de referencia lvalue. De lo contrario, el tipo de retorno es un tipo de referencia rvalue.

Si T no es un tipo referenciable (es decir, posiblemente cv-calificado void o un tipo de función con una secuencia de cv-calificadores o un calificador de referencia), el programa está mal formado.

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 Owner es 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)