Why does std::visit take a variable number of vari

2019-04-18 02:11发布

Trying to get more familiar with C++17, I've just noticed std::visit:

template <class Visitor, class... Variants>
constexpr /*something*/ visit(Visitor&& vis, Variants&&... vars);

Why does std::visit not take a single variant, but rather any number of variants? I mean, you can always take some standard library function and have it take multiple parameters with the same role, working on all of them (e.g. std::find() for multiple elements in a container); or you could be taking multiple visitors and using them on the same variant.

So, why this specific 'variadification'?

2条回答
姐就是有狂的资本
2楼-- · 2019-04-18 02:58

To make multiple visitation cleaner. Let's say I had two std::variant<A,B>, one named left and one named right. With multiple visitation, I can write:

struct Visitor {
    void operator()(A, A);
    void operator()(A, B);
    void operator()(B, A);
    void operator()(B, B);
};

std::visit(Visitor{}, left, right);

That's a pretty clean interface, and is something that's pretty commonly useful. It's also easy to implement efficiently - you just create a n-dimensional array of functions instead of a one dimensional array.

On the other hand, with only single visitation, you'd have to write:

std::visit([&](auto l_elem){
    std::visit([&](auto r_elem){
        Visitor{}(l_elem, r_elem);
    }, right)
}, left);

That's miserable to write, miserable to read, and likely less efficient too.

查看更多
欢心
3楼-- · 2019-04-18 03:00

Because we need to allow for visitation of combinations of classes within variants. That is, if we have

using Var1 = std::variant<A,B>;
using Var2 = std::variant<C,D>;

we can obviously use these kinds of visitors:

struct Visitor1 {
    void operator()(A);
    void operator()(B);
};

struct Visitor2 {
    void operator()(C);
    void operator()(D);
};

with Var1 and Var2 respectively. We can even use this next kind, with both Var1 and Var2 individually:

struct Visitor3 {
    void operator()(A);
    void operator()(B);
    void operator()(C);
    void operator()(D);
};

but what OP is missing is that we want to be able to visit one of the four pairs (A,C), (A,D), (B,C), (B,D) - when looking at a pair of Var1 and Var2 together. That's why the variadic argument to std::visit is all-but-necessary. An appropriate visitor would look like this:

struct Visitor4 {
    void operator()(A,C);
    void operator()(A,D);
    void operator()(B,C);
    void operator()(B,D);
};

and we would call std::visit(Visitor4{}, my_var1_instance, my_var2_instance);

I figured this out when reading Barry's answer.

查看更多
登录 后发表回答