Why your code didn't work
The template parameters port
and pin
are just formal parameters. E.g. for regular functions, would you expect that
void foo(int x) { /* bla */ }
and
void foo(int y) { /* meh */ }
to be different from each other? In fact, you can simply declare such functions in a header by their signature void foo(int)
only. To overload functions you need to provide different arguments.
With template parameters, it works exactly the same
template<typename port> class invert { /* bla */ };
and
template<typename pin> class invert { /* bla */ };
have the same template signature and can in fact be declared as template<typename> class invert;
inside a header.
Here, the analogy breaks down because class templates don't overload, but have to be specialized. This means that your two versions of invert
need to be "different enough" in their parameter structure, regardless of what you write in the class implementation itself.
How to fix it
To get what you want, you actually need to specialize at least one of the tempaltes. The typical way to do that has been explained on here before, and is to define a traits class and use SFINAE to select at compile-time:
template <typename T>
class has_n_pins
{
typedef char one;
typedef long two;
template <typename C> static one test( typeof(&C::n_pins) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template<typename T, bool = has_n_pins<T>::value >
class invert: public T
{
// your current port implementation
};
// specialization for types T that don't have a n_pins() member function
template<typename T>
class invert<T, false>: public T
{
// your current pin implementation
};
Now there are two specializations: invert<T, true>
(all T
that have n_pins()
) and invert<T, false>
(all T
that don't). The compiler will now be able to select the appropriate one depending on the template argument you actually provided.