如何在 for 循环中使用 const 变量来生成模板类?

NSK*_*NSK 15 c++ templates for-loop compile-time-constant template-classes

我有一个像

template <size_t N>
class A
{
    template <size_t N>
    someFunctions() {};
};
Run Code Online (Sandbox Code Playgroud)

现在我想创建类的实例并在 for 循环中为一组许多值调用其中的函数,例如

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

Run Code Online (Sandbox Code Playgroud)

这该怎么做?希望有一种方法可以做到这一点。

Gui*_*cot 11

这将需要称为 a 的东西template for,它是扩展语句将采用的预期形式,它看起来像一个 for 循环,但实际上是一个函数中的模板块,它被多次实例化。

当然,有一个解决方法。我们可以滥用泛型 lambda 来声明某种本地模板化块并自己实例化它:

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>{})), ...);
}
Run Code Online (Sandbox Code Playgroud)

这个函数接受一个整数序列,并F根据序列的长度实例化 lambda 。

它是这样使用的:

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();
});
Run Code Online (Sandbox Code Playgroud)

在这里,N可以作为模板参数发送,因为它是一个具有 constexpr 转换运算符到整数类型的对象。更准确地说,它的std::integral_constant价值越来越大。

活生生的例子

  • 啊。当我看到这样的模板乐趣时,我只知道稍后我必须在没有调用堆栈的情况下调试它,并且必须猜测发生了什么......:) (3认同)
  • @Ayxan 避免当 lambda `f` 返回重载逗号运算符的类型时出现问题 (2认同)

JeJ*_*eJo 6

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();
}
Run Code Online (Sandbox Code Playgroud)

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`
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

Output:

1
2
3
4
5
6
7
8
9
10
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

Outputs the same as the above version.


Ayx*_*xan 1

实现此目的的一种方法是使用模板元编程,如下所示:

#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>{}(); }
Run Code Online (Sandbox Code Playgroud)