¿Cómo tener una variable constante en un bucle for para la generación de clases de plantilla?


15

Tengo un código como

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

Ahora quiero crear instancias de la clase y llamar a las funciones en un bucle for para un conjunto de muchos 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 hacer esto? Esperando un método para hacer esto.


Para ser utilizado como un parámetro de plantilla Ndebe ser constexprcuál si es una variable de bucle ese no es el caso
CoryKramer

No puedes, ¿A realmente necesita ser una plantilla?
Alan Birtles

Sí, es necesario que la clase A sea una plantilla por algunas razones y es un modelo de algo, por lo que tiene que ser una clase de plantilla
Nachiappan venkatesh

Respuestas:


11

Esto requeriría algo llamado a, template forque es la declaración de expansión de formulario esperada que tomarán las , que es algo que parece un bucle for, pero en realidad es un bloque con plantilla en una función que se instancia varias veces.

Por supuesto, hay una solución alternativa. Podemos abusar de lambdas genéricas para declarar algún tipo de bloque local con plantilla e instanciarlo nosotros mismos:

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

Esta función toma una secuencia de enteros e instancia la lambda Ftantas veces como la longitud de la secuencia.

Se usa así:

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

Aquí, Nse puede enviar como parámetro de plantilla porque es un objeto que tiene un operador de conversión constexpr a un tipo entero. Más precisamente, es std::integral_constantun valor creciente.

Ejemplo en vivo


3
Ugh Cuando veo plantillas divertidas como esta, sé que voy a tener que depurarlas más tarde sin una pila de llamadas y adivinar qué está pasando ... :)
Michael Dorgan

¿Para qué sirve static_cast<void>?
Ayxan

2
@Ayxan evita problemas cuando la lambda fdevuelve un tipo que sobrecarga al operador de coma
Guillaume Racicot

@MichaelDorgan Es por eso que necesitamos template for. Abusar de construcciones de lenguaje como esta siempre es más doloroso
Guillaume Racicot

@GuillaumeRacicot o necesitamos mejores abstracciones que las plantillas para meta programación.
Ajay Brahmakshatriya

5

La Nnecesidad de ser constante en tiempo de compilación, que es con un forbucle normal, no es posible.

Pero, hay muchas soluciones alternativas. Por ejemplo, inspirado en esta publicación SO , puede hacer algo como lo siguiente. ( Ver una demostración en 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 1a100


En , lo anterior se puede reducir a una sola AGeneratorclase de plantilla (es decir, se puede evitar la especialización), utilizando if constexpr. ( Ver una demostración en 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`
        }
    }
};

Salida :

1
2
3
4
5
6
7
8
9
10

En caso de proporcionar el rango de iteración, puede usar lo siguiente. ( Ver una demostración en 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();
}

Produce lo mismo que la versión anterior.


4

Desde C ++ 20, puede usar plantillas lambdas, por lo que puede probar algo de la siguiente manera

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

El siguiente es un ejemplo completo de compilación que imprime todos los números del 0 al 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

Una forma de hacerlo es con una metaprogramación de plantilla con algo como esto:

#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 para completar: ¿es realmente necesario que la clase o la función tengan una plantilla, si el único uso de la función se llama desde el bucle?

Si es así y no quieres escribir a mano, mira boost.hana.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.