4

In that test code:

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

Why do I have to write template <typename T> class Signal;?

Why do I have to specify it ?

2
  • Because you have to have a primary template in order to be able to specialize one. In this case the primary template is not implemented.
    – Praetorian
    Commented Dec 14, 2012 at 23:27
  • You don't have to. You can make the primary template parametrized by T and U if you prefer. That's just not as flexible.
    – Kerrek SB
    Commented Dec 14, 2012 at 23:42

2 Answers 2

3

You don't have to do what you're doing, but it's the most flexible approach. A one-parameter template with specialization goes like this:

  • A template parametrized on one type...

    template <typename> struct Foo;
    
  • ... but it is only defined for function types:

    template <typename R>
    struct Foo<R()> { /* ... */ };
    
    template <typename R, typename A1>
    struct Foo<R(A1)> { /* ... */ };
    
    template <typename R, typename ...Args>
    struct Foo<R(Args...)> { /* ... */ };
    

The alternative would be to hard-code the signature of the function:

  • A class template that stores the function pointer of a one-argument function:

    template <typename R, typename A>
    struct Bar
    {
        R (*fp)(A);
        Bar(R(*f)(A)) : fp(f) { }
        // ...
    };
    

As you can see, the first approach is much more general, as we can specialize Foo for any function type we like. By contrast, the direct template in the second example is intricately tied to the details of the function signature and cannot easily be generalized.

2

You're creating a specialization of Signal that combines the arbitrary types T and U into the form T(U). This is put together as the specialization Signal<T(U)>: only one type is in the parameter, which why we forward-declared the Signal taking only one type T. This wouldn't be possible without that declaration.

Here's a simple example:

template <typename T> struct A;

template <typename T, typename U> struct A<T(U)> {

};

int main() {

    A<void(int)> a;

}

The types void and int are bound to the types T and U respectively. This is combined into the type void(int) used in the primary declaration of A to specialize the class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.