Як мати змінну const у циклі for для створення шаблонів класів?


15

Я маю подібний код

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

Тепер я хочу створити екземпляри класу та викликати функції в ньому у циклі 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();
    }
}

Як це зробити? Сподіваючись на спосіб зробити це.


Для використання в якості параметра шаблону Nпотрібно бути, constexprякий, якщо це
циклічна

Ви не можете, чи дійсно потрібно бути шаблоном?
Алан

Так, потрібно, щоб клас A був шаблоном з якихось причин, і це модель чогось, тому він повинен бути шаблоновим класом
nachiappan venkatesh

Відповіді:


11

Для цього знадобиться щось, що називається a, template for- це очікувані заяви про розширення форми , які є схожими на цикл for, але насправді це шаблонний блок у функції, яка інстанцірується кілька разів.

Звичайно, є рішення. Ми можемо зловживати загальними лямбдами, щоб оголосити якийсь локальний блок шаблонів та інстанціювати його самостійно:

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

Ця функція займає цілу послідовність і інстанціює лямбда Fстільки ж разів, скільки довжина послідовності.

Він використовується так:

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

Тут Nможе бути надіслано як параметр шаблону, оскільки це об'єкт, який має оператор перетворення constexpr для цілого типу. Точніше, це std::integral_constantзі зростаючим значенням.

Живий приклад


3
Тьфу. Коли я бачу такий шаблон веселощів, я просто знаю, що мені доведеться пізніше налагодити його без
Michael

Яка мета static_cast<void>?
Айксан

2
@Ayxan уникає проблем, коли лямбда fповертає тип, який перевантажує оператор комами
Гійом Рачикот

@MichaelDorgan Саме тому нам це потрібно template for. Зловживання мовними конструкціями, подібними до цього, завжди болючіше
Гійом Рачикот

@GuillaumeRacicot або нам потрібні кращі абстракції, ніж шаблони для метапрограмування.
Аджай Брахмакшатрія

5

Ці Nпотреби бути постійними у час компіляції, який з нормальним forцикл не представляється можливим.

Але, існує багато обхідних шляхів. Наприклад, надихнувшись цим повідомленням SO , ви можете зробити щось на кшталт наступного. ( Дивіться демо-версію )

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

Друкує 1до100


У вищезазначене можна звести до одного AGeneratorкласу шаблонів (тобто спеціалізації можна уникнути), використовуючи if constexpr. ( Дивіться демо-версію )

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

Вихід :

1
2
3
4
5
6
7
8
9
10

У разі надання діапазону ітерації ви можете скористатися наступним. ( Дивіться демо-версію )

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

Виходи такі ж, як у наведеній версії.


4

З C ++ 20 ви можете використовувати шаблонні лямбдаси, тож ви можете спробувати щось наступним чином

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

Далі наведено повний приклад компіляції, який друкує всі числа від 0 до 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

Один із способів зробити це можна за допомогою мета-програмування шаблонів приблизно так:

#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

Просто для повноти - чи дійсно потрібно для шаблону класу чи функції, якщо єдине використання функції потрібно викликати з циклу?

Якщо так, і ви не хочете писати від руки, подивіться на boost.hana.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.