迭代不同的类型

nwp*_*nwp 62 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;
Run Code Online (Sandbox Code Playgroud)

我想要show一堆物品:

for (auto &obj : {w3, w4, w5, t1})
    obj.show();
Run Code Online (Sandbox Code Playgroud)

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

Nik*_*iou 58

在现代C++中,您将使用折叠表达式来"遍历"应用成员函数的异构参数:

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

Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3);
Run Code Online (Sandbox Code Playgroud)

演示

您可以在我的博客中阅读更多相关内容

  • @RichardHodges你没有比这更现代化 (7认同)
  • 注意:"现代"在这里意味着c ++ 1z或更好. (6认同)

Ric*_*ges 39

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

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

Window
Widget
Widget
Toolbar
Run Code Online (Sandbox Code Playgroud)

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

// 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)... };

}
Run Code Online (Sandbox Code Playgroud)

如此称呼:

for_all([](auto& thing) { thing.show(); }, w3, w4, w5, t1);
Run Code Online (Sandbox Code Playgroud)

  • @MaximEgorushkin :)这是适合使用c风格演员的少数几次之一,但我可以修改它以便在必要时不使用它.如果您的仿函数返回一个值(然后未使用),那么强制转换可以抑制编译器警告 (5认同)
  • 你需要在braced-init-list中使用`(void)`强制转换来抑制重载的逗号. (3认同)
  • 我想知道你为什么:1.使用trailing-return-type只需要一次,并且使用所有可能性的`main`.2.不要利用main中隐含的`return 0;`. (3认同)
  • 有趣的是你如何考虑`boost :: fusion` oldskool,但你使用C风格的演员.双重标准? (2认同)
  • @MaximEgorushkin我当然同意你的看法。在几乎所有代码中,c样式转换都没有位置。static_cast版本如下:`static_cast &lt;void&gt;(expand {0,(static_cast &lt;void&gt;(objects.show()),0)...});`不确定是否提高清晰度或降低清晰度。你怎么看? (2认同)
  • 老实说,@ Deduplicator非常懒惰。我有一个名为skeleton.cpp的自编译模板文件,我将其复制到新文件中以尝试这些小演示。然后我将文件推送到一个名为“ play”的git repo中,以防万一有人在几周后我完全忘记我在说什么时问我一个问题。 (2认同)

Max*_*kin 21

另一种选择是使用boost::tupleor std::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(); } 
    );
Run Code Online (Sandbox Code Playgroud)

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


nwp*_*nwp 15

基于/sf/answers/482610551/,这可以在不创建额外功能,增强或继承的情况下工作.

标题:

#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);
  }
Run Code Online (Sandbox Code Playgroud)

的.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();
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 你在调用`make_tuple`时要复制`w3,w4,w5,t1`.复制实例只是打印它们似乎太多了.[演示](http://coliru.stacked-crooked.com/a/cf0a4518d8f87de6) (4认同)

Gin*_*lus 11

Window,WidgetToolbar共享通用接口,因此您可以创建抽象类并使其他类继承它:

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;
Run Code Online (Sandbox Code Playgroud)

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

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

看到它在线工作

  • 这带来了运行时成本(可能是调用被虚拟化),需要修改所有类,并且为每个函数创建公共基类是不可行的.另外,对于带有`.size()`的buildins,成员变量和std容器,它不起作用.但通常你是对的,这是传统的解决方案. (3认同)

Bri*_*uez 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(); });
Run Code Online (Sandbox Code Playgroud)

  • @Ruslan它位于boostorg帐户的GitHub上.我昨晚下载并安装了它到我当地的boost目录.如果你熟悉cmake,这很容易.这是一个非常好的图书馆.现在我想用它来解决一个真正的问题:) (3认同)