Есть ли способ передать шаблонную подпись функции в качестве параметра шаблона шаблона

С помощью шаблонных параметров шаблона можно передать классу шаблонный класс без указания типов по его параметрам. Мне было интересно, есть ли способ перейти к шаблону шаблона шаблона шаблонной сигнатуры функции, чтобы иметь возможность специализировать, какой вариант функции следует считать вперед.

Чтобы быть ясным - я знаю, что не могу этого сделать:

template <class T>
void foo() { /*...*/ }

template <template <class...> class FooType>
struct Foo { /*...*/ };

int main() {
    Foo<decltype(foo)> f;
}

Но каким-то образом я хотел бы получить шаблонную подпись функции до Foo. Возможно ли это?

Ответы

Ответ 1

В приведенном ниже примере у одного есть шаблонный шаблонный параметр, который принимает предпочтительную подпись для функции.
Из-за специализации и отсутствия тела для класса шаблона принимаются только типы для вызовов.
Это обобщение того, что фактически спросил ОП:

#include<cassert>

template<typename F>
struct S;

template<typename R, typename... Args>
struct S<R(Args...)> {
    using type = R(*)(Args...);
};

template<template<typename> class F>
struct T {
    typename F<void(int)>::type ft;
    typename F<double(double, double)>::type gt;
};

void f(int) { }
double g(double x, double y) { return x+y; }

int main() {
    T<S> t;
    t.ft = f;
    t.gt = g;
    t.ft(42);
    auto v = t.gt(1., 1.);
    assert(v == 2.);
}

Ответ 2

Я не мог поверить, что это невозможно, поэтому я немного искал и нашел способ сделать именно то, что хотел. Я использовал templated using с синтаксисом:

template <template<class... Args> class FooType>
struct Foo {
   FooType<int> ft;
};

template <class Res, class... Args>
using FooSignature = Res(*)(Args...);

int foo() {
   return 1;
}

int main() {
   Foo<FooSignature> f;
   f.ft = foo;
}

Однако это все еще оставляет вопрос, как это возможно, поскольку стандарт утверждает что-то противоположное.

Ответ 3

Шаблон шаблона

по-прежнему является шаблоном.

template <class T>
void foo() { /*...*/ }

template <typename T>
struct Foo { /*...*/ };

int main() {
    Foo<decltype(foo<int>)> f;
}

Ответ 4

Как можно видеть в этом ответе

Шаблон указателя функции является незаконным в С++

Стандарт С++ говорит в $14/1,

Шаблон определяет семейство классов или функций.

Дальнейшее цитирование из связанного ответа:

Обратите внимание, что он НЕ говорит: "Шаблон определяет семейство классов, функций или указателей функций"

Однако вы можете передавать конкретные указатели функций и специализироваться на их сигнатуре:

#include <iostream>

template <class T>
void foo(T) { }

template <typename>
struct Foo;

template<typename T> 
struct Foo<void(T)> 
{
    void cb() { std::cout << "T\n"; }
};

template<> 
struct Foo<void(int)> 
{
    void cb() { std::cout << "int\n"; }
};

template<> 
struct Foo<void(double)> 
{
    void cb() { std::cout << "double\n"; }
};

int main() 
{
    Foo<decltype(foo<int   >)>().cb(); // outputs 'int'
    Foo<decltype(foo<double>)>().cb(); // outputs 'double'
    Foo<decltype(foo<char  >)>().cb(); // outputs 'T'
    return 0;
}

Ответ 5

Вы не можете передать шаблон функции в качестве аргумента. Что вы можете сделать, это обернуть шаблон функции в генерации лямбда с параметром тега:

template <class T> struct tag_t { using type = T; };

template <class T>
void foo() { ... }

template <class F>
void call_func_with(F f) {
    f(tag_t<int>{} );
    f(tag_t<double>{} );
}

call_with_func([](auto tag) { foo<decltype(tag)::type>(); } );

Здесь f(tag_t<X>{} ) завершает вызов foo<X>() по желанию.