В этом тестовом коде:
#include <string>
#include <iostream>
using namespace std;
template <typename T> class Signal;
template <typename T, typename U>
class Signal<T (U)>
{
public:
Signal<T (U)>(T (*ptr)(U))
{
}
};
void Print(string const& str)
{
cout << str << endl;
}
int main(int argc, char *argv[])
{
Signal<void (string const&)> sig = &Print;
return 0;
}
Почему я должен написать template <typename T> class Signal;
?
Почему я должен это указать?
Вы создаете специализацию Signal
который объединяет произвольные типы T
а также U
в форму T(U)
, Это составлено как специализация Signal<T(U)>
: в параметре указан только один тип, поэтому мы объявили Signal
принимая только один тип T
, Это было бы невозможно без этой декларации.
Вот простой пример:
template <typename T> struct A;
template <typename T, typename U> struct A<T(U)> {
};
int main() {
A<void(int)> a;
}
Типы void
а также int
связаны с типами T
а также U
соответственно. Это объединено в тип void(int)
используется в первичной декларации A
специализировать класс.
Ты не иметь делать то, что вы делаете, но это самый гибкий подход. Однопараметрический шаблон со специализацией выглядит так:
Шаблон параметризованный по одному типу …
template <typename> struct Foo;
… но это определено только для типов функций:
template <typename R>
struct Foo<R()> { /* ... */ };
template <typename R, typename A1>
struct Foo<R(A1)> { /* ... */ };
template <typename R, typename ...Args>
struct Foo<R(Args...)> { /* ... */ };
Альтернативой было бы жестко закодировать сигнатуру функции:
Шаблон класса, в котором хранится указатель на функцию с одним аргументом:
template <typename R, typename A>
struct Bar
{
R (*fp)(A);
Bar(R(*f)(A)) : fp(f) { }
// ...
};
Как видите, первый подход гораздо более общий, так как мы можем специализироваться Foo
за любой Тип функции нам нравится. Напротив, прямой шаблон во втором примере сложно связан с деталями сигнатуры функции и не может быть легко обобщен.