Могу ли я объявить шаблон для приема только функций с однородной подписью? - PullRequest
2 голосов
/ 28 июня 2019

У меня есть набор из двух функций, каждый из которых принимает N аргументы типа T, с дополнительным аргументом типа X для второго.

Для N=2, T=int и X=std::vector<int> дублирование будет выглядеть примерно так:

void my_function2(int x1, int x2) {}
void my_function2(int x1, int x2, std::vector<int> other) {}

Я пытаюсь написать шаблонную функцию высокого порядка, которой я передаюпервая функция.

Это будет работать для N=2:

template <typename R, typename T>
int my_hof(R(*param)(T, T)) { param(1,2); }

my_hof(&my_function2); // compiles

Создание явной сигнатуры param важно, потому что в противном случае создание экземпляра шаблона завершилось бы неудачей из-за "Unresolved перегруженного типа функции».Суть в том, чтобы устранить вторую перегрузку my_function2().

Моя проблема в том, что я не могу придумать способ обобщить это значение до N.Я попытался использовать шаблон с переменными значениями:

template <typename R, typename ...X> 
struct make_signature { using type = R(*)(int, int); };
// Cheating a big on make_signature not to clutter the question
// But the idea would be to repeat the same type N times.

template <typename R, typename ...X>
int my_generalized_hof(typename make_signature<R, X...>::type param) {
    f(1,2); // cheating a bit on the call too 
}

my_generalized_hof(&my_function2); // does not compile

Я думаю, компилятор запутался, поскольку тип param не задан сразу, он не может решить, какую перегрузку использовать для вычисления типа.(«снова неразрешенный тип перегруженной функции»).Я думаю, что он также отказался бы от вычисления типа в любом случае, так как он не мог бы определить тип R.

Я не могу придумать способ генерирования подписи.

Isможно написать это на C ++?

(обратите внимание, что я знаю, что могу выбрать перегрузку, приведя my_function2() к нужному типу при вызове my_generalized_hof(), но я бы хотел этого избежать)

1 Ответ

0 голосов
/ 28 июня 2019

Я предлагаю следующее make_signature

template <typename T, std::size_t>
using getType = T;

template <typename, typename, std::size_t N,
          typename = std::make_index_sequence<N>>
struct make_signature;

template <typename R, typename T, std::size_t N, std::size_t ... Is>
struct make_signature<R, T, N, std::index_sequence<Is...>>
 { using type = R(*)(getType<T, Is>...); };

Но есть проблема: когда вы пишете

template <typename R, typename T, std::size_t N>
void my_generalized_hof (typename make_signature<R, T, N>::type param)
 {
   // do something with param
 }

часть make<R, T, N> находится в не-выводимом контексте (до ::), поэтому R, T и N не могут быть выведены из аргумента.

Я имею в виду ... вы не можете вызвать

 my_generalized_hof(&my_function2); 

Вы должны указать аргументы шаблона следующим образом

 // ...............VVVVVVVVVVVVVVV
 my_generalized_hof<void, int, 2u>(&my_function2); 

Ниже приведен полный пример компиляции

#include <utility>
#include <vector>
#include <type_traits>

void my_function2(int x1, int x2) {}
void my_function2(int x1, int x2, std::vector<int> other) {}

template <typename T, std::size_t>
using getType = T;

template <typename, typename, std::size_t N,
          typename = std::make_index_sequence<N>>
struct make_signature;

template <typename R, typename T, std::size_t N, std::size_t ... Is>
struct make_signature<R, T, N, std::index_sequence<Is...>>
 { using type = R(*)(getType<T, Is>...); };

template <typename R, typename T, std::size_t N>
void my_generalized_hof (typename make_signature<R, T, N>::type param)
 {
   // do something with param
 }

int main ()
 {
   my_generalized_hof<void, int, 2u>(&my_function2); 
 }
...