Como ter uma variável const em um loop for para a geração de classes de modelo?

15

Eu tenho um código como

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

Agora eu quero criar instâncias da classe e chamar as funções nela em um loop for para um conjunto de muitos valores como

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

Como fazer isso? Esperando um método para fazer isso.

nachiappan venkatesh
fonte
Para ser usado como um parâmetro de modelo Nprecisa ser constexpro que se é uma variável loop que não é o caso
CoryKramer
Você não pode, A realmente precisa ser um modelo?
Alan Birtles
Sim há uma necessidade para a classe A para ser modelo para algumas razões e é um modelo de algo assim que tem que ser uma classe de modelo
nachiappan Venkatesh

Respostas:

11

Isso exigiria algo chamado a, template forque é o esperado pelas instruções de expansão de forma , algo que parece um loop for, mas na realidade é um bloco de modelo em uma função que é instanciada várias vezes.

Obviamente, há uma solução alternativa. Podemos abusar de lambdas genéricas para declarar algum tipo de bloco de modelo local e instanciar ele mesmo:

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

Essa função pega uma sequência inteira e instancia o lambda Fquantas vezes o comprimento da sequência.

É usado assim:

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

Aqui, Npode ser enviado como parâmetro de modelo porque é um objeto que possui um operador de conversão constexpr para um tipo inteiro. Mais precisamente, é um std::integral_constantvalor cada vez maior.

Exemplo ao vivo

Guillaume Racicot
fonte
3
Ugh. Quando vejo divertido modelo como este, eu só sei que eu vou ter de depurá-lo mais tarde sem uma pilha de chamadas e tem que adivinhar o que está acontecendo ... :)
Michael Dorgan
Qual é o propósito static_cast<void>?
precisa saber é o seguinte
2
Evitar problemas @Ayxan quando o lambda fretorna um tipo que sobrecarrega o operador vírgula
Guillaume Racicot
@MichaelDorgan É por isso que precisamos template for. Abusando construções de linguagem como este é sempre mais dolorosa
Guillaume Racicot
@GuillaumeRacicot ou precisamos de abstrações melhores do que modelos para meta programação.
Ajay Brahmakshatriya
5

A Nnecessidade de ser constante em tempo de compilação, que é com um forloop normal, não é possível.

Mas, existem muitas soluções alternativas. Por exemplo, inspirado nesta postagem do SO , você pode fazer algo como o seguinte. ( Veja uma demonstração ao vivo )

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

Imprime 1em100


No , o acima pode ser reduzido a uma única AGeneratorclasse de modelo (ou seja, a especialização pode ser evitada), usando if constexpr. ( Veja uma demonstração ao vivo )

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

Saída :

1
2
3
4
5
6
7
8
9
10

No caso de fornecer o intervalo de iteração, você pode usar o seguinte. ( Veja uma demonstração ao vivo )

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

Produz o mesmo que a versão acima.

JeJo
fonte
4

No C ++ 20, você pode usar lambdas de modelo, para tentar algo da seguinte maneira

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

A seguir, é apresentado um exemplo completo de compilação que imprime todos os números de 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>{});
 }
max66
fonte
1

Uma maneira de fazer isso é com a meta-programação de modelos com algo como isto:

#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
fonte
0

Apenas para completar - é realmente necessário que a classe ou função seja modelada, se o único uso da função deve ser chamado de loop?

Se sim, e você não quer escrever à mão, dê uma olhada no boost.hana.

CapSel
fonte