Come avere una variabile const in un ciclo for per la generazione di classi template?


15

Ho un codice come

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

Ora voglio creare istanze della classe e chiamare le funzioni in essa contenute in un ciclo for per un insieme di molti valori come

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

Come fare questo? Spero in un metodo per farlo.


Per essere utilizzato come parametro del modello Ndeve essere constexprche, se si tratta di una variabile di loop, non è il caso
CoryKramer,

Non puoi, A deve davvero essere un modello?
Alan Birtles il

Sì, per alcuni motivi è necessario che la classe A sia un modello ed è un modello di qualcosa, quindi deve essere una classe modello
nachiappan venkatesh

Risposte:


11

Ciò richiederebbe qualcosa chiamato a template forche è la forma prevista che assumerà le dichiarazioni di espansione , che è qualcosa che sembra un ciclo for ma che in realtà è un blocco modello in una funzione che viene istanziata più volte.

Certo, c'è una soluzione alternativa. Possiamo abusare di lambda generici per dichiarare una sorta di blocco di modelli locali e istanziarlo noi stessi:

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

Questa funzione accetta una sequenza intera e crea un'istanza di lambda Ftante volte quanto la lunghezza della sequenza.

Si usa così:

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

Qui, Npuò essere inviato come parametro modello perché è un oggetto che ha un operatore di conversione constexpr in un tipo intero. Più precisamente, è std::integral_constantun valore crescente.

Esempio dal vivo


3
Ugh. Quando vedo un modello divertente come questo, so solo che dovrò eseguire il debug in un secondo momento senza un callstack e devo indovinare cosa sta succedendo ... :)
Michael Dorgan il

Qual è lo scopo di static_cast<void>?
Ayxan,

2
@Ayxan evita problemi quando lambda frestituisce un tipo che sovraccarica l'operatore virgola
Guillaume Racicot

@MichaelDorgan Questo è il motivo per cui abbiamo bisogno template for. Costruire un linguaggio abusivo in questo modo è sempre più doloroso
Guillaume Racicot il

@GuillaumeRacicot o abbiamo bisogno di astrazioni migliori rispetto ai modelli per la meta-programmazione.
Ajay Brahmakshatriya il

5

La Nnecessità di essere una costante di tempo di compilazione, che è con un forciclo normale, non è possibile.

Ma ci sono molte soluzioni alternative. Ad esempio, ispirato a questo post SO , puoi fare qualcosa di simile al seguente. ( Vedi una demo live )

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

Stampa 1su100


In , quanto sopra può essere ridotto a una singola AGeneratorclasse di template (cioè la specializzazione può essere evitata), usando if constexpr. ( Vedi una demo live )

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

Uscita :

1
2
3
4
5
6
7
8
9
10

In caso di fornire l'intervallo di iterazione, è possibile utilizzare quanto segue. ( Vedi una demo live )

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

Emette lo stesso della versione precedente.


4

Da C ++ 20, puoi usare il modello lambdas, quindi puoi provare qualcosa come segue

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

Di seguito è riportato un esempio di compilazione completa che stampa tutti i numeri da 0 a 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

Un modo per farlo è con la meta-programmazione di template con qualcosa del genere:

#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

Solo per completezza - è davvero necessario che la classe o la funzione siano template, se l'unico uso della funzione deve essere chiamato dal loop?

Se è così e non vuoi scrivere a mano dai un'occhiata a boost.hana.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.