c++ - ¿Por qué std:: visit toma un número variable de variantes?
variadic-templates c++17 (2)
Para hacer limpiador de múltiples visitas. Digamos que tenía dos std::variant<A,B>
, uno con el nombre a la left
y otro con el nombre a la right
. Con visitas múltiples, puedo escribir:
struct Visitor {
void operator()(A, A);
void operator()(A, B);
void operator()(B, A);
void operator()(B, B);
};
std::visit(Visitor{}, left, right);
Esa es una interfaz bastante limpia, y es algo que comúnmente es útil. También es fácil de implementar de manera eficiente: simplemente crea una matriz de funciones ndimensional en lugar de una matriz unidimensional.
Por otro lado, con una sola visita, tendrías que escribir:
std::visit([&](auto l_elem){
std::visit([&](auto r_elem){
Visitor{}(l_elem, r_elem);
}, right)
}, left);
Es miserable escribir, miserable leer, y probablemente menos eficiente también.
Tratando de familiarizarme con C ++ 17, me he dado cuenta de que std::visit
:
template <class Visitor, class... Variants>
constexpr /*something*/ visit(Visitor&& vis, Variants&&... vars);
¿Por qué std::visit
no toma una sola variante, sino más bien cualquier número de variantes? Quiero decir, siempre puede tomar alguna función de biblioteca estándar y hacer que tome múltiples parámetros con el mismo rol, trabajando en todos ellos (por ejemplo, std::find()
para múltiples elementos en un contenedor); o podría estar recibiendo varios visitantes y usándolos en la misma variante.
Entonces, ¿por qué esta ''variadificación'' específica?
Porque necesitamos permitir visitas de combinaciones de clases dentro de variantes. Es decir, si tenemos
using Var1 = std::variant<A,B>;
using Var2 = std::variant<C,D>;
Obviamente podemos utilizar este tipo de visitantes:
struct Visitor1 {
void operator()(A);
void operator()(B);
};
struct Visitor2 {
void operator()(C);
void operator()(D);
};
con Var1
y Var2
respectivamente. Incluso podemos usar este tipo siguiente, con Var1
y Var2
individualmente:
struct Visitor3 {
void operator()(A);
void operator()(B);
void operator()(C);
void operator()(D);
};
pero lo que falta es que queramos poder visitar uno de los cuatro pares (A,C)
, (A,D)
, (B,C)
, (B,D)
- cuando miramos un par de Var1
y Var2
juntos . Es por eso que el argumento variad para std::visit
es todo menos necesario. Un visitante apropiado se vería así:
struct Visitor4 {
void operator()(A,C);
void operator()(A,D);
void operator()(B,C);
void operator()(B,D);
};
y llamaríamos std::visit(Visitor4{}, my_var1_instance, my_var2_instance);
Me di cuenta de esto al leer la respuesta de Barry .