Почему 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);

я понял это, когда читал Барри.