54

やりたい

template<typename... ArgTypes> void print(ArgTypes... Args)
{
   print(Args)...;
}

そして、この非常にかさばる再帰チェーンと同等にします:

template<typename T, typename... ArgTypes> void print(const T& t, ArgTypes... Args)
{
  print(t);
  print(Args...);
}

その後に、印刷したいすべてのタイプの明示的な単一パラメーターの特殊化が続きます。

再帰実装の「問題」は、多くの冗長なコードが生成されることです。これは、各再帰ステップがN-1引数の新しい関数になるためです。一方、私が望むコードは、単一のN-argprint関数のコードのみを生成します。せいぜいN特殊なprint機能しか持たない。

4

3 に答える 3

84

C++17 フォールド式

(f(args), ...);

オーバーロードされたコンマ演算子を使用してオブジェクトを返す可能性のあるものを呼び出す場合:

((void)f(args), ...);

C++17 より前のソリューション

ここでの典型的なアプローチは、ダムリスト初期化子を使用し、その中で展開を行うことです:

{ print(Args)... }

カーリー初期化子では、評価の順序は左から右に保証されます。

しかし、print戻ってくるvoidので、それを回避する必要があります。それではintにしましょう。

{ (print(Args), 0)... }

ただし、これはステートメントとして直接機能しません。タイプを与える必要があります。

using expand_type = int[];
expand_type{ (print(Args), 0)... };

Argsこれは、パック内に常に 1 つの要素がある限り機能します。サイズがゼロの配列は有効ではありませんが、常に少なくとも 1 つの要素を持つようにすることで回避できます。

expand_type{ 0, (print(Args), 0)... };

このパターンをマクロで再利用可能にすることができます。

namespace so {
    using expand_type = int[];
}

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) ::so::expand_type{ 0, ((PATTERN), 0)... }

// usage
SO_EXPAND_SIDE_EFFECTS(print(Args));

ただし、これを再利用可能にするには、いくつかの詳細にもう少し注意を払う必要があります。ここではオーバーロードされたカンマ演算子を使用したくありません。いずれかの引数でコンマをオーバーロードできないvoidため、それを利用しましょう。

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) \
        ::so::expand_type{ 0, ((PATTERN), void(), 0)... }

ゼロの大きな配列を無駄に割り当てるコンパイラを恐れている場合は、そのようにリスト初期化できるが何も格納しない他の型を使用できます。

namespace so {
    struct expand_type {
        template <typename... T>
        expand_type(T&&...) {}
    };
}
于 2013-06-27T09:51:14.507 に答える
7

さらにシンプルで読みやすいアプローチを使用できます

template<typename... ArgTypes> void print(ArgTypes... Args)
{
   for (const auto& arg : {Args...})
   {
      print(arg);
   }
}

私はコンパイルエクスプローラーで両方のバリアントを試してみましたが、O3 または O2 を使用した gcc と clang の両方でまったく同じコードが生成されましたが、私のバリアントは明らかにクリーンです。

于 2016-06-17T09:16:19.387 に答える