为什么std :: visit采用可变数量的变体?

ein*_*ica 20 c++ std variant variadic-templates c++17

为了更熟悉C++ 17,我刚才注意到std::visit:

template <class Visitor, class... Variants>
constexpr /*something*/ visit(Visitor&& vis, Variants&&... vars);
Run Code Online (Sandbox Code Playgroud)

为什么不std::visit采用单一变体,而是采用任意数量的变体?我的意思是,你总是可以采用一些标准的库函数,让它采用具有相同角色的多个参数,对所有这些参数进行处理(例如,std::find()对于容器中的多个元素); 或者你可以带多个访问者并在同一个变体上使用它们.

那么,为什么这个特定的"变化"?

Bar*_*rry 27

使多个访问更清洁.假设我有两个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);
Run Code Online (Sandbox Code Playgroud)

这是一个非常干净的界面,并且非常有用.它也很容易有效地实现 - 您只需创建一个n维数组函数而不是一维数组.

另一方面,只有一次访问,你必须写:

std::visit([&](auto l_elem){
    std::visit([&](auto r_elem){
        Visitor{}(l_elem, r_elem);
    }, right)
}, left);
Run Code Online (Sandbox Code Playgroud)

写作悲惨,阅读悲惨,效率也可能低.

  • @einpoklum你通过阅读我的答案找到了答案,但我的答案并没有帮助你弄清楚答案? (6认同)

ein*_*ica 3

因为我们需要允许访问变体中的类组合。也就是说,如果我们有

using Var1 = std::variant<A,B>;
using Var2 = std::variant<C,D>;
Run Code Online (Sandbox Code Playgroud)

显然我们可以使用这些类型的访问者:

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

struct Visitor2 {
    void operator()(C);
    void operator()(D);
};
Run Code Online (Sandbox Code Playgroud)

分别与Var1Var2。我们甚至可以使用下一种,同时使用Var1Var2单独使用:

struct Visitor3 {
    void operator()(A);
    void operator()(B);
    void operator()(C);
    void operator()(D);
};
Run Code Online (Sandbox Code Playgroud)

但 OP 缺少的是,当我们一起查看一对和时,我们希望能够访问四对(A,C),,, -中的一对。这就是为什么可变参数 to几乎是必要的。合适的访问者应该是这样的:(A,D)(B,C)(B,D)Var1Var2 std::visit

struct Visitor4 {
    void operator()(A,C);
    void operator()(A,D);
    void operator()(B,C);
    void operator()(B,D);
};
Run Code Online (Sandbox Code Playgroud)

我们会打电话给std::visit(Visitor4{}, my_var1_instance, my_var2_instance);

我在阅读巴里的回答时发现了这一点。