Можете ли вы вызвать функцию шаблона, созданную во время компиляции, с переменной, указанной во время выполнения? - PullRequest
3 голосов
/ 01 августа 2020

У меня есть функция с целочисленным параметром шаблона:

template<int N>
int func() {
    return N;
}

Я явно создаю экземпляр шаблона для всех аргументов шаблона, которые я мог бы предоставить:

template int func<1>();
template int func<2>();
template int func<3>();
//...

Затем я хочу вызвать fun c с целым числом, указанным во время выполнения n:

//This code does not compile    
int main() {
        int n;
        std::cin >> n;
        // check that n is one of the allowed values
        std::cout << func<n>(); // can fail if given a bad n
        return 0;
    }

Могу ли я внести изменения в этот код, чтобы вызвать func<n>() с n, указанным во время выполнения (скажем, 0 < n < 20)?

Ответы [ 4 ]

4 голосов
/ 01 августа 2020

Да, это возможно, но не совсем так, как вы пытаетесь, потому что синтаксис шаблона, например F<N>, требует значений времени компиляции. Однако после создания они являются обычными функциями, и вы можете принимать на них указатели функций или помещать их в std :: function, et c.

Вы можете, например, создать массив для указателей функций которые разрешают доступ по индексу времени выполнения, примерно так:

template <int N>
int f() { return N; }  // your function(s)

int main()
{
    using Func = int(*)(); 
    Func funcs[]{f<0>, f<1>, f<2>, };  // array of instances of f

    // call a function via runtime value stored in n:
    int n = 2;
    funcs[n]();
}
3 голосов
/ 02 августа 2020

Если вы можете использовать C ++ 17, другой способ использования сворачивания

#include <utility>
#include <iostream>

template <int N>
int func()
 { return N; }

template <int ... Is>
int func2 (int n, std::integer_sequence<int, Is...>)
 {
   int ret {};

   if ( false == ( ... || (Is == n ? (ret = func<Is>(), true) : false) ) )
      throw std::runtime_error{"no matching func()"};

   return ret;
 }

template <int I = 20>
int func3 (int n)
 { return func2(n, std::make_integer_sequence<int, I>{}); }

int main()
 {
   int n;

   std::cin >> n;

   std::cout << func3(n) << std::endl;
 }

Начиная с C ++ 20, вы также можете использовать лямбда-выражения шаблонов, поэтому вы также можете написать

#include <utility>
#include <iostream>

template <int N>
int func()
 { return N; }

int main()
 {
   int n;

   std::cin >> n;

   std::cout << []<int ... Is>(int n, std::integer_sequence<int, Is...>)
    {
      int ret {};

      if ( false == ( ... || (Is == n ? (ret = func<Is>(), true) : false) ) )
         throw std::runtime_error{"no matching func()"};

      return ret;
    }(n, std::make_integer_sequence<int, 20>{}) << std::endl;
 }
3 голосов
/ 01 августа 2020

Вот решение с простой функцией вызывающего абонента, позволяющей вызывать вашу забаву c при использовании целых чисел от 1 до 20

#include <cassert>
#include <iostream>

template<int N>
int func() { return N; }

template <int N>
int callFunc(int n)
{
  if (n==N) return func<N>();
  else return callFunc<N-1>(n);
}

template <>
int callFunc<0>(int n)
{
  assert(false);
  return 0;
}

#define MAX_N 20
int main() {
  int n;
  std::cin >> n;
  std::cout << callFunc<MAX_N>(n);
  return 0;
}
3 голосов
/ 01 августа 2020

Дайте исполняющей программе список функций на выбор, и пусть она выбирает, используя индекс ( живой пример ):

template<std::size_t... Indices>
int call_func_impl(std::index_sequence<Indices...>, int n) {
    using FuncType = int(*)();
    static const FuncType funcs[] = {func<Indices>...};
    return funcs[n]();
}

template<int MaxN>
int call_func(int n) {
    return call_func_impl(std::make_index_sequence<MaxN>{}, n);
}

...

call_func<20>(n);

Естественно, вы можете создать массив вручную ( func<0>, func<1>, ...), но это немного лучше. Если вам нужна последовательность, отличная от 0 до N-1, используйте std::integer_sequence. Если вам нужна непоследовательность, готового помощника нет, хотя вы можете адаптировать один из вышеперечисленных, если есть шаблон для получения желаемых значений (например, умножьте каждый элемент index_sequence на 2, чтобы получить четные числа) .

Для полноты картины C ++ 20 предлагает прекрасный (по крайней мере, если вы привыкли к худшему) шаблон для уменьшения количества аргументов копирования и вставки:

template<int MaxN>
int call_func(int n) {
    const auto impl = [n]<std::size_t... Indices>(std::index_sequence<Indices...>) {
        using FuncType = int(*)();
        static const FuncType funcs[] = {func<Indices>...};
        return funcs[n]();
    };

    return impl(std::make_index_sequence<MaxN>{});
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...