テンプレートクラスを生成するためのforループにconst変数を含めるにはどうすればよいですか?


15

私のようなコードがあります

template <size_t N>
class A
{
    template <size_t N>
    someFunctions() {};
};

次に、クラスのインスタンスを作成し、そのような関数をforループで呼び出して、次のような多くの値のセットを取得します

// in main()

int main()
{
    for (int i = 1; i <= 100; i++)
    {
        const int N = i;  // dont know how to do this
        A<N> a;
        a.functionCalls();
    }
}

これを行う方法?これを行う方法を期待しています。


テンプレートパラメータとして使用されるべきNであることが必要constexprそれがケースではありませんループ変数である場合
CoryKramer

できません。Aは本当にテンプレートである必要がありますか?
Alan Birtles

ええ、何らかの理由でクラスAをテンプレートにする必要があり、それは何かのモデルであるため、テンプレートクラスでなければなりません
nachiappan venkatesh

回答:


11

これにtemplate forは、拡張ステートメントが取ると予想されるフォームであるa と呼ばれるものが必要になります。これはforループのように見えますが、実際には、複数回インスタンス化される関数内のテンプレートブロックです。

もちろん、回避策があります。ジェネリックラムダを悪用して、ある種のローカルテンプレートブロックを宣言し、それをインスタンス化することができます。

template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F f) {
    (static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}

この関数は整数シーケンスを取り、シーケンスFの長さと同じ回数だけラムダをインスタンス化します。

これは次のように使用されます:

for_sequence(std::make_index_sequence<100>(), [](auto N) { /* N is from 0 to 99 */
  A<N + 1> a; /* N + 1 is from 1 to 100 */
  a.functionCalls();
});

ここでは、Nそれは整数型へconstexprの変換演算子を持つオブジェクトですので、テンプレートパラメータとして送信することができます。より正確には、それstd::integral_constantは増加する値を持つものです。

実例


3
ああ。このようなテンプレートの楽しみを見ると、コールスタックなしで後でデバッグする必要があり、何が起こっているのかを推測する必要があることを知っています... :)
Michael Dorgan

の目的はstatic_cast<void>何ですか?
Ayxan

2
@Ayxanは、ラムダfがコンマ演算子をオーバーロードする型を返す場合の問題を回避します
Guillaume Racicot

@MichaelDorganこれが必要な理由ですtemplate for。このような言語構成を乱用することは常により
困難です

@GuillaumeRacicotまたはメタプログラミング用のテンプレートよりも優れた抽象化が必要です。
Ajay Brahmakshatriya

5

N正常であるコンパイル時定数であることが必要forループは不可能です。

しかし、多くの回避策があります。たとえば、このSOの投稿に触発されて、次のようなことができます。 ライブデモをご覧ください

template<size_t N>
class A
{
public:
    // make the member function public so that you can call with its instance
    void someFunctions()
    {
        std::cout << N << "\n";
    };
};

template<int N> struct AGenerator
{
    static void generate()
    {
        AGenerator<N - 1>::generate();
        A<N> a;
        a.someFunctions();
    }
};

template<> struct AGenerator<1>
{
    static void generate()
    {
        A<1> a;
        a.someFunctions();
    }
};

int main()
{
    // call the static member for constructing 100 A objects
    AGenerator<100>::generate();
}

印刷1する100


、上記1つのテンプレートに還元することができるAGenerator使用して、(すなわち、特殊化を回避することができる)クラスif constexprライブデモをご覧ください

template<std::size_t N>
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (N == 1)
        {
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
        else
        {
            AGenerator<N - 1>::generate();
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
    }
};

出力

1
2
3
4
5
6
7
8
9
10

反復の範囲を提供する場合、以下を使用できます。ライブデモをご覧ください

template<std::size_t MAX, std::size_t MIN = 1> // `MIN` is set to 1 by default
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (MIN == 1)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
        else if constexpr (MIN != 1 && MIN <= MAX)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
    }
};

int main()
{
    // provide the `MAX` count of looping. `MIN` is set to 1 by default
    AGenerator<10>::generate();
}

上記バージョンと同じ出力です。


4

C ++ 20以降では、テンプレートラムダを使用できるため、次のように試すことができます

[]<int ... Is>(std::integer_sequence<int, Is...>)
 { (A<Is+1>{}.functionCall(), ...); }
   (std::make_integer_sequence<int, 100>{});

以下は、0から99までのすべての数値を出力する完全なコンパイル例です。

#include <utility>
#include <iostream>

int main()
 {
  []<int ... Is>(std::integer_sequence<int, Is...>)
   { (std::cout << Is << std::endl, ...); }
     (std::make_integer_sequence<int, 100>{});
 }

1

これを行う1つの方法は、次のようなテンプレートメタプログラミングを使用することです。

#include <iostream>

template <std::size_t N>
struct A {
  void foo() { std::cout << N << '\n'; }
};

template <std::size_t from, std::size_t to>
struct call_foo {
  void operator()() {
    if constexpr (from != to) {
      A<from + 1>{}.foo();
      call_foo<from + 1, to>{}();
    }
  }
};

int main() { call_foo<0, 100>{}(); }

0

完全性のために-関数の唯一の使用がループから呼び出される場合、クラスまたは関数がテンプレート化されることは本当に必要ですか?

もしそうなら、あなたが手で書きたくない場合はboost.hanaを見てください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.