Namespaces
Variants

std:: visit

From cppreference.net
Utilities library
Definido en el encabezado <variant>
template < class Visitor, class ... Variants >
constexpr /* ver más abajo */ visit ( Visitor && v, Variants && ... values ) ;
(1) (desde C++17)
template < class R, class Visitor, class ... Variants >
constexpr R visit ( Visitor && v, Variants && ... values ) ;
(2) (desde C++20)
Plantillas auxiliares
template < class ... Ts >
auto && as - variant ( std:: variant < Ts... > & value ) ;
(3) ( solo para exposición* )
template < class ... Ts >
auto && as - variant ( const std:: variant < Ts... > & value ) ;
(4) ( solo para exposición* )
template < class ... Ts >
auto && as - variant ( std:: variant < Ts... > && value ) ;
(5) ( solo para exposición* )
template < class ... Ts >
auto && as - variant ( const std:: variant < Ts... > && value ) ;
(6) ( solo para exposición* )

Aplica el visitante v (un Callable que puede ser invocado con cualquier combinación de tipos de Variants) a los Variants values .

Dado VariantBases como decltype ( as-variant ( std:: forward < Variants > ( values ) ) ... (un paquete de sizeof... ( Variants ) tipos):

1) Invoca v como si fuera mediante

INVOKE ( std:: forward < Visitor > ( v ) ,
std :: get < indices > ( std:: forward < VariantBases > ( values ) ) ... )
,

donde indices es as-variant ( values ) . index ( ) ... .
2) Invoca v como si fuera mediante

INVOKE<R> ( std:: forward < Visitor > ( v ) ,
std :: get < indices > ( std:: forward < VariantBases > ( values ) ) ... )
,

donde indices es as-variant ( values ) . index ( ) ... .

Estas sobrecargas participan en la resolución de sobrecarga solo si cada tipo en VariantBases es un tipo válido. Si la expresión denotada por INVOKE o INVOKE<R> (desde C++20) es inválida, o los resultados de INVOKE o INVOKE<R> (desde C++20) tienen tipos diferentes o categorías de valor para diferentes indices , el programa está mal formado.

3-6) Las plantillas de función de solo exposición as-variant aceptan un valor cuyo tipo puede ser deducido para std:: variant < Ts... > (es decir, ya sea std:: variant < Ts... > o un tipo derivado de std:: variant < Ts... > ), y devuelven el valor std::variant con la misma calificación de const y categoría de valor.
3,4) Devuelve value .
5,6) Devuelve std :: move ( value ) .

Contenidos

Parámetros

v - un Callable que acepta cada alternativa posible de cada variante en Variants
values - lista de variantes para pasar al visitante

Valor de retorno

1) El resultado de la operación INVOKE . El tipo de retorno es el tipo obtenido al aplicar decltype al resultado.
2) Nada si R es (posiblemente calificado con cv) void ; de lo contrario, el resultado de la operación INVOKE<R> .
3-6) Un valor std::variant convertido desde value .

Excepciones

Lanza std::bad_variant_access si as-variant ( value_i ) . valueless_by_exception ( ) es true para cualquier variant value_i en values .

Complejidad

Cuando el número de variantes es cero o uno, la invocación del objeto invocable se implementa en tiempo constante; es decir, no depende del número de tipos que pueden almacenarse en el variant.

Si el número de variantes es mayor que uno, la invocación del objeto invocable no tiene requisitos de complejidad.

Notas

Sea n igual a ( 1 * ... * std:: variant_size_v < std:: remove_reference_t < VariantBases >> ) , las implementaciones generalmente generan una tabla equivalente a un arreglo (posiblemente multidimensional) de n punteros a función para cada especialización de std::visit , lo cual es similar a la implementación de funciones virtuales .

Las implementaciones también pueden generar una sentencia switch con n ramas para std::visit (por ejemplo, la implementación de MSVC STL utiliza una sentencia switch cuando n no es mayor que 256).

En implementaciones típicas, la complejidad temporal de la invocación de v puede considerarse igual a la del acceso a un elemento en un arreglo (posiblemente multidimensional) o la ejecución de una sentencia switch.

Macro de prueba de características Valor Estándar Característica
__cpp_lib_variant 202102L (C++23)
(DR17)
std::visit para clases derivadas de std::variant

Ejemplo

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
// la variante a visitar
using value_t = std::variant<int, long, double, std::string>;
// tipo auxiliar para el visitante #4
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// guía de deducción explícita (no necesaria a partir de C++20)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
int main()
{
    std::vector<value_t> vec = {10, 15l, 1.5, "hola"};
    for (auto& v: vec)
    {
        // 1. void visitor, solo se llama para efectos secundarios (aquí, para E/S)
        std::visit([](auto&& arg){ std::cout << arg; }, v);
        // 2. visitor que retorna un valor, demuestra el modismo de retornar otra variante
        value_t w = std::visit([](auto&& arg) -> value_t { return arg + arg; }, v);
        // 3. visitante de coincidencia de tipos: una lambda que maneja cada tipo de manera diferente
        std::cout << ". Después de duplicar, la variante contiene ";
        std::visit([](auto&& arg)
        {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                std::cout << "int con valor " << arg << '\n';
            else if constexpr (std::is_same_v<T, long>)
                std::cout << "long con valor " << arg << '\n';
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "double con valor " << arg << '\n';
            else if constexpr (std::is_same_v<T, std::string>)
                std::cout << "std::string con valor " << std::quoted(arg) << '\n';
            else
                static_assert(false, "¡visitante no exhaustivo!");
        }, w);
    }
    for (auto& v: vec)
    {
        // 4. otro tipo de visitor de coincidencia de tipos: una clase con 3 operator() sobrecargados
        // Nota: El operador de plantilla `(auto arg)` se vinculará a `int` y `long`
        //       en este caso, pero en su ausencia el operador `(double arg)` operator()
        //       *también* se vinculará a `int` y `long` porque ambos son implícitamente
        //       convertible to double. Al usar esta forma, se debe tener cuidado
        //       que las conversiones implícitas se manejan correctamente.
        std::visit(overloaded{
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
        }, v);
    }
}

Salida:

10. Después de duplicar, variant contiene int con valor 20
15. Después de duplicar, variant contiene long con valor 30
1.5. Después de duplicar, variant contiene double con valor 3
hello. Después de duplicar, variant contiene std::string con valor "hellohello"
10 15 1.500000 "hello"

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
LWG 2970 C++17 el tipo de retorno de la sobrecarga (1) no preservaba la
categoría de valor del resultado de la operación INVOKE
preserva
LWG 3052
( P2162R2 )
C++17 los efectos no estaban especificados si algún tipo
en Variants no es un std::variant
especificado

Véase también

(C++26)
llama al functor proporcionado con el argumento contenido por el variant
(función miembro pública)
intercambia con otro variant
(función miembro pública)