Namespaces
Variants

std:: forward_like

From cppreference.net
Utilities library
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 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 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 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)