Почему std:: visit принимает переменное количество вариантов?
пытаясь познакомиться с C++17, я только что заметил std::visit:
template <class Visitor, class... Variants>
constexpr /*something*/ visit(Visitor&& vis, Variants&&... vars);
Почему std::visit не принимать один вариант, а варианты? Я имею в виду, вы всегда можете взять некоторую стандартную библиотечную функцию и заставить ее принимать несколько параметров с одной и той же ролью, работая над всеми из них (например,std::find() для нескольких элементов в контейнере); или вы можете принимать несколько посетителей и использовать их в одном варианте.
так, почему эта специфическая "вариадификация"?
2 ответов
сделать множественный уборщик посещения. Допустим, у меня было два std::variant<A,B>, один по имени left и один по имени right. При многократном посещении я могу написать:
struct Visitor {
void operator()(A, A);
void operator()(A, B);
void operator()(B, A);
void operator()(B, B);
};
std::visit(Visitor{}, left, right);
это довольно чистый интерфейс, и это то, что довольно часто полезно. Это также легко реализовать эффективно - вы просто создаете n-мерный массив функций вместо одномерного массива.
С другой стороны, только с одним посещением вам придется пиши:
std::visit([&](auto l_elem){
std::visit([&](auto r_elem){
Visitor{}(l_elem, r_elem);
}, right)
}, left);
это жалко писать, жалко читать и, вероятно, менее эффективно.
потому что нам нужно учитывать посещение комбинации классов в вариантах. То есть, если у нас есть
using Var1 = std::variant<A,B>;
using Var2 = std::variant<C,D>;
мы, очевидно, можем использовать эти виды посетителей:
struct Visitor1 {
void operator()(A);
void operator()(B);
};
struct Visitor2 {
void operator()(C);
void operator()(D);
};
С Var1 и Var2 соответственно. Мы даже можем использовать этот следующий вид, с обоими Var1 и Var2 индивидуально:
struct Visitor3 {
void operator()(A);
void operator()(B);
void operator()(C);
void operator()(D);
};
но чего не хватает OP, так это того, что мы хотим иметь возможность посетить одну из четырех пар (A,C), (A,D), (B,C), (B,D) - при взгляде на пару Var1 и Var2 вместе. Вот почему вариационный аргумент std::visit - все-но-надо. Соответствующий посетитель будет выглядеть так:
struct Visitor4 {
void operator()(A,C);
void operator()(A,D);
void operator()(B,C);
void operator()(B,D);
};
и мы могли бы назвать std::visit(Visitor4{}, my_var1_instance, my_var2_instance);
я понял это, когда читал Барри.