web-dev-qa-db-ja.com

さまざまなタイプの繰り返し

次のコードを考えます:

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();

ただし、for- loopのstd::initializer_list<T>Tを推定できないため、実際にはTが収まらないため、これはコンパイルされません。必要なコード量と不必要な実行時オーバーヘッドのため、型消去型を作成したくありません。概念リスト内のすべての項目に対してobjの型が個別に推定されるように、ループを正しく記述する方法を教えてください。

62
nwp

現代のC++では、fold式を使用して、メンバー関数を適用して異種の引数を「ウォークスルー」します。

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

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

デモ

詳細については、私の blog をご覧ください。

58

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;
}

期待される出力:

Window
Widget
Widget
Toolbar

別のより一般的な方法(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)... };

}

次のように呼ばれます:

for_all([](auto& thing) { thing.show(); }, w3, w4, w5, t1);
39
Richard Hodges

別のオプションは、boost::Tupleまたはstd::Tupleおよび boost::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のメソッドのAssembly出力を上記と比較しました。 gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native生成されたアセンブリコードは同一です。

21

https://stackoverflow.com/a/6894436/348457 に基づいて、これは追加の関数、ブーストまたは継承を作成せずに機能します。

ヘッダ:

#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();
    });
}
15
nwp

WindowWidget、およびToolbarは共通のインターフェースを共有するため、抽象クラスを作成し、他のクラスにそれを継承させることができます。

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();
}

オンラインでの作業を参照

11
GingerPlusPlus

Boost.Hana をお勧めします。これは、IMHOが利用可能な最良かつ最も柔軟なテンプレートメタプログラミングライブラリです。

#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(); });
8
Brian Rodriguez

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;
}
4
Mikhail

遅い答えですが、ここは一般的なソリューションであり、C++ 14は boost::fusion::for_each のように動作しますが、ブーストが必要:

#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);
}

2番目の例の欠点は、最初に処理関数を指定する必要があるため、よく知られている std::for_each と同じ外観を持たないことです。とにかく、-O2最適化レベルを使用するコンパイラ(GCC 5.4.0)では、同じ Assembly output を生成します。

0
Akira