Bagaimana cara membuat variabel const dalam for for untuk pembuatan kelas template?

15

Saya punya kode seperti

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

Sekarang saya ingin membuat instance kelas dan memanggil fungsi-fungsi di dalamnya dalam for loop untuk sekumpulan banyak nilai seperti

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

Bagaimana cara melakukannya? Berharap ada metode untuk melakukan ini.

nachiappan venkatesh
sumber
Agar dapat digunakan sebagai parameter templat, yang Ndiperlukan constexprjika itu adalah variabel perulangan, bukan itu masalahnya
CoryKramer
Anda tidak bisa, apakah A benar-benar harus menjadi templat?
Alan Birtles
Ya ada kebutuhan untuk kelas A untuk menjadi templat karena beberapa alasan dan itu adalah model dari sesuatu sehingga harus menjadi kelas templat
nachiappan venkatesh

Jawaban:

11

Ini akan membutuhkan sesuatu yang disebut template foryang merupakan bentuk yang diharapkan dari pernyataan ekspansi , yang merupakan sesuatu yang terlihat seperti for loop tetapi pada kenyataannya adalah blok templated dalam fungsi yang di-instanciated beberapa kali.

Tentu saja, ada solusinya. Kami dapat menyalahgunakan lambdas generik untuk mendeklarasikan semacam blok templated lokal dan instansikan sendiri:

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

Fungsi ini membutuhkan urutan bilangan bulat dan instantiate lambda Fsebanyak waktu sebagai panjang urutan.

Digunakan seperti ini:

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

Di sini, Ndapat dikirim sebagai parameter templat karena objek yang memiliki operator konversi constexpr ke tipe integer. Lebih tepatnya, ini std::integral_constantdengan nilai yang meningkat.

Contoh langsung

Guillaume Racicot
sumber
3
Ugh. Ketika saya melihat templat menyenangkan seperti ini, saya hanya tahu saya harus men-debugnya nanti tanpa callstack dan harus menebak apa yang sedang terjadi ... :)
Michael Dorgan
Apa tujuannya static_cast<void>?
Ayxan
2
@Ayxan menghindari masalah saat lambda fmengembalikan jenis yang membebani operator koma
Guillaume Racicot
@MichaelDorgan Inilah sebabnya mengapa kita perlu template for. Konstruksi bahasa yang menyalahgunakan seperti ini selalu lebih menyakitkan
Guillaume Racicot
@GuillaumeRacicot atau kita membutuhkan abstraksi yang lebih baik daripada templat untuk pemrograman meta.
Ajay Brahmakshatriya
5

The Nkebutuhan untuk kompilasi-waktu konstan, yang dengan normal forlingkaran tidak mungkin.

Tapi, ada banyak solusi. Misalnya, terinspirasi oleh pos SO ini , Anda dapat melakukan sesuatu seperti berikut ini. ( Lihat demo Langsung )

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

Mencetak 1ke100


Dalam , yang di atas dapat direduksi menjadi satu AGeneratorkelas templat (mis. Spesialisasi dapat dihindari), menggunakan if constexpr. ( Lihat demo Langsung )

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

Keluaran :

1
2
3
4
5
6
7
8
9
10

Jika memberikan kisaran iterasi, Anda dapat menggunakan yang berikut ini. ( Lihat demo Langsung )

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

Outputnya sama dengan versi di atas.

JeJo
sumber
4

Dari C ++ 20, Anda dapat menggunakan templat lambdas, sehingga Anda dapat mencoba sesuatu sebagai berikut

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

Berikut ini adalah contoh kompilasi lengkap yang mencetak semua angka dari 0 hingga 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>{});
 }
maks66
sumber
1

Salah satu cara Anda dapat melakukan ini adalah dengan meta-pemrograman template dengan sesuatu seperti ini:

#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>{}(); }
Ayxan
sumber
0

Hanya untuk kelengkapan - apakah itu benar-benar diperlukan untuk kelas atau fungsi templated, jika satu-satunya penggunaan fungsi dipanggil dari loop?

Jika demikian dan Anda tidak ingin menulis dengan tangan, perhatikan boost.hana.

CapSel
sumber