如何在C++17中使用“变体”和“访问”?

问题描述 投票:0回答:1

我需要在 A、B、C 类 上测试 a()、b()、c()。最简单的使用方法是要求 3*3 if-else。我想试试 3+3 if-else.

Class A
{
...
public:
int k(){...};
...
};

Class B
{
...
public:
int k(){...};
...
};

Class C
{
...
public:
int k(){...};
...
};

template<class T>
void a(const T& g, int m)
{
...
g.k();
...
}

template<class T>
void b(const T& g, int m, int n)
{
...
g.k();
...
}

template<class T>
void c(const T& g, int m, int n, int o)
{
...
g.k();
...
}

然后看到variant,用它来实现3+3 if-else

int main(int argc, char** argv)
{
std::variant<std::monostate, A, B, C> x;//For some reason A, B, C do not have the default constructors
if(argv[1]=="A") x=A(...);
else if(argv[1]=="B") x=B(...);
else if(argv[1]=="C") x=C(...);

//error 1 How to use a function that takes more than two arguments directly here?
if(argv[2]=="a") std::visit(a, x, atoi(argv[2]));
else if if(argv[2]=="b") std::visit(b, x, atoi(argv[2]), atoi(argv[3]));
else if(argv[2]=="c") std::visit(c, x, atoi(argv[2]), atoi(argb[3]), atoi(argv[4]));

//error 2 Since A, B, C do not have the default constructors, monostate does not have the k() required by a(), b(), c().
if(argv[2]=="a") std::visit([&](const auto& G){a(G, atoi(argv[2]));}, x);
else if if(argv[2]=="b") ...;
else if(argv[2]=="c") ...;
}

请找到更好的方法或解决上面的error1和error2,非常感谢。

但无论如何,我实际上是想看看是否有更好的方法来编写它来避免 3*3 if-else。

//i want to avoid
int main(int argc, char** argv)
{
   if(argv[1]=="A")
   {
      A tmp_class;
      if(argv[2]=="a") a(tmp_class);
      else if(argv[2]=="b") b(tmp_class);
      else c(tmp_class);
   }
   else if(argv[1]=="B")
   {
      B tmp_class;
      if(argv[2]=="a") a(tmp_class);
      else if(argv[2]=="b") b(tmp_class);
      else c(tmp_class);
   }
   else
   {
      C tmp_class;
      if(argv[2]=="a") a(tmp_class);
      else if(argv[2]=="b") b(tmp_class);
      else c(tmp_class);
   }
}

是否有更好的方法达到 3+3 if-else?

    //pseudocode
    int main(int argc, char** argv)
    {
       T tmp_class;
       //3 if-else
       if(argv[1]=="A") tmp_class=A(...);
       else if(argv[1]=="B") tmp_class=B(...);
       else tmp_class=C(...);
       //+
       //3 if-else
       if(argv[2]=="a") a(tmp_class);
       else if(argv[2]=="b") b(tmp_class);
       else c(tmp_class);
    }
c++ c++17 variant
1个回答
2
投票

使用

std::visit
的最干净的方法是创建一个
overloaded
struct
派生自您创建的每个
lambda
以处理您的
std::variant
在任何给定时间可能持有的所有潜在类型。

在我展示的示例中,我没有定义任何构造函数,所以

std::monostate
不是明确需要的,但我还是添加了它以向您展示如何处理
std::variant
没有被赋值的情况;


template<class... Ts> 
struct overloaded : Ts... { using Ts::operator()...; };
// Deduction guide is only needed pre C++20.
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

struct A {
    void print(const std::string_view msg) const {
        std::cout << msg << '\n';
    }
};
struct B {};
struct C {};

using types = std::variant<std::monostate, A, B, C>;

int main()
{
    // You can either assign an already created object to the variant.
    types values = B{};

    // Or you can create the object in place.
    values.emplace<A>(/* Here you can pass constructor arguments if required. */);

    const std::string_view msg{ "'A' class" };

    std::visit(overloaded {
        // Note that I'm capturing the variable 'msg' and using it in the lambda.
        [&msg](const A& a) { a.print(msg); },
        [](const B& b) { std::cout << "B\n"; },
        [](const C& c) { std::cout << "C\n"; },
        [](std::monostate) { std::cout << "Nothing\n"; }
        }, values);

    // Note that you can also use the following free functions
    // to check/get the object of a specific type from the variant.
    if (std::holds_alternative<A>(values)) {
        // Holds 'A' so lets use std::get to get a reference to the object.     
        const A& a{ std::get<A>(values) };
    }

    if (const auto* ptr{ std::get_if<B>(&values) }) {
        // Holds 'B'.
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.