Почему 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);
я понял это, когда читал Барри.