Почему std:: visit принимает переменное количество вариантов?
Попытка познакомиться с С++ 17, я только что заметил std::visit
:
template <class Visitor, class... Variants>
constexpr /*something*/ visit(Visitor&& vis, Variants&&... vars);
Почему std::visit
не брать ни одного варианта, а сколько угодно вариантов? Я имею в виду, что вы всегда можете использовать некоторую стандартную библиотечную функцию и иметь несколько параметров с одинаковой ролью, работая над всеми из них (например, std::find()
для нескольких элементов в контейнере); или вы можете брать нескольких посетителей и использовать их в одном и том же варианте.
Итак, почему эта конкретная "вариация"?
Ответы
Ответ 1
Потому что нам нужно разрешить посещать комбинации классов в вариантах. То есть, если мы имеем
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);
Я понял это при чтении ответа Барри.
Ответ 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);
Это жалко писать, жалко читать и, вероятно, менее эффективно.