How to have a const variable in a for loop for the

2020-06-08 14:35发布

I have a code like

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

Now I want to create instances of the class and call the functions in it in a for loop for a set of many values like

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

How to do this? Hoping for a method to do this.

5条回答
Ridiculous、
2楼-- · 2020-06-08 15:15

Just fo completeness - is it really required for the class or function be templated, if the only usage of function is to be called from loop?

If so and you don't want to write by hand take look at boost.hana.

查看更多
兄弟一词,经得起流年.
3楼-- · 2020-06-08 15:22

The N needs to be compile-time constant, which is with a normal for loop is not possible.

But, there are many workarounds. For instance, inspired by this SO post, you can do something like the following. (See a Live demo)

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

Prints 1 to 100


In , the above can be reduced to a single template AGenerator class(i.e. specialization can be avoided), using if constexpr. (See a Live demo)

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

Output:

1
2
3
4
5
6
7
8
9
10

In case of providing the range of iteration, you could use the following.(See a Live demo)

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

Outputs the same as the above version.

查看更多
霸刀☆藐视天下
4楼-- · 2020-06-08 15:27

From C++20, you can use template lambdas, so you can try something as follows

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

The following is a full compiling example that print all numbers from 0 to 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>{});
 }
查看更多
forever°为你锁心
5楼-- · 2020-06-08 15:30

One way you can do this is with template meta-programming with something like this:

#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>{}(); }
查看更多
家丑人穷心不美
6楼-- · 2020-06-08 15:37

This would require something called a template for which is the expected form expansion statements will take, which is something that look like a for loop but in reality is a templated block in a function that is instanciated multiple times.

Of course, there is a workaround. We can abuse generic lambdas to declare some sort of local templated block and instanciate it ourself:

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

This function takes an integer sequence and instantiate the lambda F as many time as the length of the sequence.

It is used like this:

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

Here, N can be sent as template parameter because it's an object that has a constexpr conversion operator to an integer type. More precisely, it's a std::integral_constant with an increasing value.

Live example

查看更多
登录 后发表回答