C++ 变体访问重载函数

Hur*_*ent 7 c++ variant

我想在变体上执行重载函数。以下代码块可以工作并编译,但visit调用似乎过于复杂。为什么我不能简单地写:

std::visit(&f, something);
Run Code Online (Sandbox Code Playgroud)

工作版本和上下文:

#include <variant>
#include <string>
#include <iostream>
#include <functional>

struct A {
        std::string name = "spencer";
};

struct B {
        std::string type = "person";
};

struct C {
        double age = 5;
};

void f(A a) {
        std::cout << a.name << std::endl;
}

void f(B b) {
        std::cout << b.type << std::endl;
}

void f(C c) {
        std::cout << c.age << std::endl;
}


int main() {
        std::variant<A, B, C> something{B{}};
        std::visit([](auto&& x) {f(x);}, something);
}
Run Code Online (Sandbox Code Playgroud)

有没有更简单的方法?

Sil*_*olo 10

std::visit(&f, something);
Run Code Online (Sandbox Code Playgroud)

这是无效的,因为f它不是一个单一的函数。&f说“给我一个指向f”。但这f不是一回事;它是三个函数,它们碰巧共享一个名称,并具有三个单独的指针。

std::visit([](auto&& x) {f(x);}, something);
Run Code Online (Sandbox Code Playgroud)

这将创建一个基于模板的闭包,该模​​板生成在编译时进行分派的代码。实际上,它的工作原理就像我们所做的一样

void f(A a) {
  std::cout << a.name << std::endl;
}

void f(B b) {
  std::cout << b.type << std::endl;
}

void f(C c) {
  std::cout << c.age << std::endl;
}

struct F {  
  template<typename T>
  void operator()(T x) {
    f(x);
  }
};

int main() {
  std::variant<A, B, C> something{B{}};
  std::visit(F(), something);
}
Run Code Online (Sandbox Code Playgroud)

这将迫使 C++ 编译器在模板扩展期间生成类似的内容

void f(A a) {
  std::cout << a.name << std::endl;
}

void f(B b) {
  std::cout << b.type << std::endl;
}

void f(C c) {
  std::cout << c.age << std::endl;
}

struct F {
  void operator()(A x) {
    f(x);
  }
  void operator()(B x) {
    f(x);
  }
  void operator()(C x) {
    f(x);
  }
};

int main() {
  std::variant<A, B, C> something{B{}};
  std::visit(F(), something);
}
Run Code Online (Sandbox Code Playgroud)

如果你想消除 lambda 包装器,你需要一个可以作为参数传递的可调用对象,而函数指针是不够的,因为函数指针无法进行重载解析。我们总是可以显式地创建一个函子对象。

struct F {
  void operator()(A a) {
    std::cout << a.name << std::endl;
  }
  void operator()(B b) {
    std::cout << b.type << std::endl;
  }
  void operator()(C c) {
    std::cout << c.age << std::endl;
  }
};

int main() {
  std::variant<A, B, C> something{B{}};
  std::visit(F(), something);
}
Run Code Online (Sandbox Code Playgroud)

您是否认为这种方法比以前的方法更干净取决于您。一方面,它更像传统的 OOP 访问者模式,因为我们有一个对象进行访问。另一方面,如果我们可以传递函数名称并且让 C++ 理解我们的意思,那就std::visit太好了,但这要么需要特殊的 C++ 语法,要么需要多方法形式的运行时调度。不管怎样,这不太可能很快发生,或者根本不可能发生。