迭代不同的类型

时间:2015-12-16 14:08:45

标签: c++ c++11 c++14

给出以下代码:

struct Window{
    void show();
    //stuff
}w1, w2, w3;

struct Widget{
    void show();
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show();
    //stuff
}t1, t2, t3;

我想show一堆项目:

for (auto &obj : {w3, w4, w5, t1})
    obj.show();

然而,这不会编译,因为std::initializer_list<T> - 循环中的for无法推断T,实际上并不存在适合的T。我不想创建类型擦除类型,因为需要大量的代码和不必要的运行时开销。如何正确编写循环以便分别为概念列表中的每个项推导出obj的类型?

8 个答案:

答案 0 :(得分:58)

在现代C ++中,您使用折叠表达式,以及#34;走过&#34;应用成员函数的异构参数:

auto Printer = [](auto&&... args) {
    (args.show(), ...);
};

Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3);

Demo

您可以在我的blog

中详细了解相关信息

答案 1 :(得分:39)

boost :: fusion很棒但是oldskool - 它迎合了c ++ 03中的不足。

c ++ 11的可变参数模板扩展到救援!

atom-typescript-plugin

预期产出:

#include <iostream>

struct Window{
    void show() {
        std::cout << "Window\n";
    }
    //stuff
}w1, w2, w3;

struct Widget{
    void show() {
        std::cout << "Widget\n";
    }
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show()
    {
        std::cout << "Toolbar\n";
    }
    //stuff
}t1, t2, t3;


template<class...Objects>
void call_show(Objects&&...objects)
{
    using expand = int[];
    (void) expand { 0, ((void)objects.show(), 0)... };
}

auto main() -> int
{
    call_show(w3, w4, w5, t1);
    return 0;
}

另一种更通用的方式(需要c ++ 14):

Window
Widget
Widget
Toolbar

这样称呼:

// note that i have avoided a function names that look like
// one in the standard library.

template<class Functor, class...Objects>
void for_all(Functor&& f, Objects&&... objects)
{
    using expand = int[];
    (void) expand { 0, (f(std::forward<Objects>(objects)), 0)... };

}

答案 2 :(得分:21)

另一种选择是使用boost::tuplestd::tupleboost::fusion::for_each算法:

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>

boost::fusion::for_each(
    boost::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), // by reference, not a copy
    [](auto&& t) { t.show(); } 
    );

出于好奇,将Richard Hodges的方法生成的装配输出与上述方法进行了比较。使用gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native生成的汇编代码是相同的。

答案 3 :(得分:15)

基于https://stackoverflow.com/a/6894436/3484570,这可以在不创建额外功能,增强或继承的情况下工作。

部首:

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(const std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &&, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>&& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(std::move(t), f);
  }

的.cpp:

struct Window{
    void show(){}
    //stuff
}w1, w2, w3;

struct Widget{
    void show(){}
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show(){}
    //stuff
}t1, t2, t3;

int main() {
    for_each(std::tie(w3, w4, w5, t1), [](auto &obj){
        obj.show();
    });
}

答案 4 :(得分:11)

WindowWidgetToolbar共享公共接口,因此您可以创建抽象类并使其他类继承它:

struct Showable {
    virtual void show() = 0; // abstract method
};

struct Window: Showable{
    void show();
    //stuff
}w1, w2, w3;

struct Widget: Showable{
    void show();
    //stuff
}w4, w5, w6;

struct Toolbar: Showable{
    void show();
    //stuff
}t1, t2, t3;

然后,您可以创建指向Showable的指针数组,并迭代它:

int main() {
    Showable *items[] = {&w3, &w4, &w5, &t1};
    for (auto &obj : items)
        obj->show();
}

See it working online

答案 5 :(得分:8)

我推荐Boost.Hana,其中恕我直言是最好,最灵活的模板元编程库。

#include <boost/hana/ext/std/tuple.hpp>
#include <boost/hana.hpp>

namespace hana = boost::hana;

hana::for_each(std::tie(w3, w4, w5, t1), [](auto& obj) { obj.show(); });

答案 6 :(得分:4)

我认为boost::variant值得一提。它更有可能成为C ++ 17中的std::variant

int main()
{
  std::vector<boost::variant<Window*, Widget*, Toolbar*>> items = { &w1, &w4, &t1 };

  for (const auto& item : items)
  {
    boost::apply_visitor([](auto* v) { v->show(); }, item);
  }
  return 0;
}

答案 7 :(得分:0)

一个迟到的答案,但这里是一般解决方案,C ++ 14与boost::fusion::for_each类似,但不需要Boost:

#include <tuple>

namespace detail {
template<typename Tuple, typename Function, std::size_t... Is>
void tuple_for_each_impl(Tuple&& tup, Function&& fn, std::index_sequence<Is...>) {
    using dummy = int[];
    static_cast<void>(dummy {
        0, (static_cast<void>(fn(std::get<Is>(std::forward<Tuple>(tup)))), 0)...
    });
}
}

template<typename Function, typename... Args>
void tuple_for_each(std::tuple<Args...>&& tup, Function&& fn) {
    detail::tuple_for_each_impl(std::forward<std::tuple<Args...>>(tup),
            std::forward<Function>(fn), std::index_sequence_for<Args...>{});
}

int main() {
    tuple_for_each(std::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), [](auto&& arg) {
        arg.show();
    });
}

如果您希望在没有std::tuple的情况下实现或多或少相同的功能,您可以创建上述代码的单功能变体:

#include <utility>

template<typename Function, typename... Args>
void va_for_each(Function&& fn, Args&&... args) {
    using dummy = int[];
    static_cast<void>(dummy {
        0, (static_cast<void>(fn(std::forward<Args>(args))), 0)...
    });
}

int main() {
    auto action = [](auto&& arg) { arg.show(); };
    va_for_each(action, w1, w2, w3, w4, w5, w6, t1, t2, t3);
}

第二个例子的缺点是它需要首先指定处理函数,因此不具有与众所周知的std::for_each相同的外观。无论如何,使用我的编译器(GCC 5.4.0)使用-O2优化级别,它们会产生相同的assembly output