Estoy tratando de acceder al contenido de una variante. No sé qué hay allí, pero afortunadamente, la variante sí. Entonces pensé en preguntarle a la variante en qué índice está y luego usar ese índice parastd::get
su contenido.
Pero esto no compila:
#include <variant>
int main()
{
std::variant<int, float, char> var { 42.0F };
const std::size_t idx = var.index();
auto res = std::get<idx>(var);
return 0;
}
El error ocurre en la std::get
llamada:
error: no matching function for call to ‘get<idx>(std::variant<int, float, char>&)’
auto res = std::get<idx>(var);
^
In file included from /usr/include/c++/8/variant:37,
from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: ‘template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
get(std::pair<_Tp1, _Tp2>& __in) noexcept
^~~
/usr/include/c++/8/utility:216:5: note: template argument deduction/substitution failed:
main.cpp:9:31: error: the value of ‘idx’ is not usable in a constant expression
auto res = std::get<idx>(var);
^
main.cpp:7:15: note: ‘std::size_t idx’ is not const
std::size_t idx = var.index();
^~~
¿Cómo puedo arreglar esto?
Respuestas:
Esencialmente, no puedes.
Tu escribiste:
... pero solo en tiempo de ejecución, no en tiempo de compilación.
Y eso significa que su
idx
valor no es tiempo de compilación.Y eso significa que no puedes usar
get<idx>()
directamente.Algo que podría hacer es tener una declaración de cambio; feo, pero funcionaría:
Sin embargo, esto es bastante feo. Como sugieren los comentarios, usted también podría
std::visit()
(que no es muy diferente del código anterior, excepto usar argumentos de plantilla variables en lugar de ser tan explícito) y evitar el cambio por completo. Para otros enfoques basados en índices (no específicos destd::variant
), consulte:¿Idioma para simular parámetros de plantilla numérica en tiempo de ejecución?
fuente
El compilador necesita saber el valor
idx
en el momento de la compilación parastd::get<idx>()
trabajar, ya que se está utilizando como argumento de plantilla.Primera opción: si el código debe ejecutarse en tiempo de compilación, haga todo
constexpr
:Esto funciona porque
std::variant
esconstexpr
amigable (sus constructores y métodos son todosconstexpr
).Segunda opción: si el código no está destinado a ejecutarse en tiempo de compilación, lo cual es probable, el compilador no puede deducir en tiempo de compilación el tipo de
res
, porque podría ser tres cosas diferentes (int
,float
ochar
). C ++ es un lenguaje de tipo estático, y el compilador debe poder deducir el tipo de aauto res = ...
partir de la expresión que sigue (es decir, siempre debe ser del mismo tipo).Puede usar
std::get<T>
, con el tipo en lugar de un índice, si ya sabe cuál será:En general, use
std::holds_alternative
para verificar si la variante contiene cada uno de los tipos dados y manejarlos por separado:Alternativamente puedes usar
std::visit
. Esto es un poco más complicado: puede usar una función lambda / plantilla que sea independiente del tipo y funcione para todos los tipos de variantes, o pasar un functor con un operador de llamada sobrecargado:Ver std :: visit para detalles y ejemplos.
fuente
El problema es que
std::get<idx>(var);
requieren (paraidx
) un valor conocido de tiempo de compilación.Entonces un
constexpr
valorPero para inicializar
idx
comoconstexpr
, tambiénvar
tenía que serconstexpr
fuente
El problema surge de la creación de instancias de plantillas en tiempo de compilación, mientras que el índice que está obteniendo se calcula en tiempo de ejecución. Del mismo modo, los tipos de C ++ también se definen en tiempo de compilación, por lo que incluso con la
auto
declaración,res
debe tener un tipo concreto para que el programa esté bien formado. Esto significa que incluso sin la restricción en la plantilla, lo que está tratando de hacer es inherentemente imposible para expresiones no constantesstd::variant
. ¿Cómo se solucionaría esto?En primer lugar, si su variante es una expresión constante, el código se compila y funciona como se esperaba
De lo contrario, deberá utilizar algún mecanismo de ramificación manual.
Puede definir estas ramas utilizando el patrón de visitante, consulte std :: visit .
fuente
Esto es inherentemente imposible en el modelo de C ++; considerar
¿Cuál
f
se llamaf<int>
of<double>
? Si es "ambos", eso significa queg
contiene una rama (que no lo hace), o que hay dos versiones deg
(que simplemente empuja el problema a su llamador). Y piense:f(T,U,V,W)
¿dónde se detiene el compilador?En realidad, hay una propuesta para un JIT para C ++ que permitiría cosas como esta compilando esas versiones adicionales de
f
cuándo se llaman, pero es muy temprano.fuente