【问题标题】:Iterating over tuple in C++17/20 [duplicate]在 C++17/20 中迭代元组 [重复]
【发布时间】:2019-02-11 22:59:41
【问题描述】:

有没有人知道在 C++17 / 20 中迭代元组的好方法?假设我们有一些这样的代码:

class Test
{
    public:
        Test( int x ) : x_(x) {};
        void Go() const { std::cout << "Hi!" << x_ << "\n" ; }
        int x_;
};
int main()
{
    std::tuple tplb{ Test{1} , Test{2} ,  Test{3} };
}

我们如何使用最新的 17/20 功能遍历元组并在每个元组上调用 Go() 方法?

我知道你可以只拥有一个对象的向量,然后它就可以轻松工作。我的目标是能够拥有某种多态性而不必使用虚函数。

这个想法是能够在元组中拥有支持相同方法的其他对象类型。如果该方法存在于每个对象中,则代码将编译和执行,而无需使用基类、虚拟对象、vtable 等。

std::applystd::invoke 有什么办法吗?

【问题讨论】:

  • @mistertribs 这个问题是关于 C++11 的,所以它现在已经过时了。
  • @Brian - 不过那里的一些答案适用于 C++17。
  • 是的,我认为/希望在 17/21 中有一些新的结构可以让这变得非常优雅。在没有虚拟的情况下在编译时强制执行这样的多态性有多酷?并且没有五页难以阅读的模板工作。
  • @mistertribs 我同意,特别是 xskxzr 的答案明确针对 C++17。但我认为这个问题不应该被标记为与那个问题重复。
  • 我确实读过那个 - 我希望有一些更好的 c++17/21 解决方案,允许直接调用元组中的对象的方法。

标签: c++ tuples c++17


【解决方案1】:

std::applystd::invoke 有什么办法吗?

std::apply 确实符合折叠表达式的需要:

std::tuple tplb{ Test{1} , Test{2} ,  Test{3} };

std::apply([](const auto&... tests){(tests.Go(), ...);}, tplb);

在这里,我们为tuple 的每个类型值调用方法Go()

这个想法是能够在元组中拥有支持相同方法的其他对象类型。如果该方法存在于每个对象中,则代码将编译和执行,而无需使用基类、虚拟对象、vtable 等。

所以上述方法有效。 如果您会更进一步并根据类型分派到不同的实现,您可以使用来自std::visit's exampleoverloaded 类:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

auto f = overloaded {
        [](const Test& test) { test.Go(); },
        [](double d) { std::cout << d << ' '; },
        [](const std::string& s) { std::cout << s << ' '; },
    };
std::apply([&](const auto&... e){ (f(e), ...);}, my_tuple);

【讨论】:

  • 泰,这太棒了!次要注意,这是:“std::apply([](const auto&... tests){(test.Go(), ...);}, tplb);”需要是“std::apply([](const auto&... tests) {(tests.Go(), ...); }, tplb);”。
  • 这让我使用第二个完全不相关但实现“Go()”方法的类。太酷了,又来了。
  • using 声明 Ts::operator()... 的目的是什么?既然Ts...是公开派生的,那是不是不需要调整operator()成员函数的可访问性?
  • 似乎应该在折叠表达式中调用f 而不是overloaded。目前,这试图从 my_tuple 包含的任何内容中派生,然后忽略构造的结果。
  • @303:如果没有using Ts::operator()...;,调用将是模棱两可的。
【解决方案2】:

需要std::index_sequence 诡计才能访问元组的每个成员。

#include <iostream>
#include <tuple>
#include <utility>

class Test
{
    public:
        Test( int x ) : x_(x) {}; 
        void Go() const { std::cout << "Hi!" << x_ << "\n" ; } 
        int x_; 
};



template<typename F, typename T, std::size_t... Index>
void doStuffIndex(F&& action, T const& tup, std::index_sequence<Index...> const&)
{
    bool ignore[] = {((std::get<Index>(tup).*action)(), true)...};
}

template<typename F, typename... Obj>
void doStuff(F&& action, std::tuple<Obj...> const& tup)
{
    doStuffIndex(action, tup, std::make_index_sequence<sizeof...(Obj)>());
}

int main()
{
    std::tuple<Test, Test, Test> tplb{ Test{1} , Test{2} ,  Test{3} };
    doStuff(&Test::Go, tplb);
}

【讨论】:

  • 对此表示赞同,但不是我要问的。看看下面的答案,就明白了。请注意,在您的解决方案中,您仍然引用特定的 Class::Method()。这限制了在不同类上调用相同方法的能力。下面来自 Jarod42 的答案是肯定的。
猜你喜欢
  • 2021-05-27
  • 1970-01-01
  • 1970-01-01
  • 2013-09-11
  • 2011-07-08
  • 2020-05-04
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
相关资源
最近更新 更多