Title is a little ambiguous.
Lets say I have a template defined as:
template < typename T >
void foo ( int x ) ;
template <>
void foo<char> ( int x ) ;
template <>
void foo<unsigned char> ( int x ) ;
template <>
void foo<short> ( int x ) ;
...
Internally both foo<signed>()
and foo<unsigned>()
do exactly the same thing. The only requirement is that T
be an 8bit type.
I could do this by creating another template to type define a standard type based on size.
template < typename T, size_t N = sizeof( T ) > struct remap ;
template < typename T, size_t > struct remap< 1 >
{
typedef unsigned char value;
}
...
Note, function templates cannot have default parameters. This solution only relocates the problem to another template and also introduces a problem if somebody tried passing a struct type as a parameter.
What is the most elegant way to solve this without repeating those function declarations?
This is not a C++11 question.
One possibility is to specialize a class template for multiple types at once:
// from: http://en.cppreference.com/w/cpp/types/enable_if
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
template < typename A, typename B >
struct is_same
{
static const bool value = false;
};
template < typename A >
struct is_same<A, A>
{
static const bool value = true;
};
template < typename T, typename dummy = T >
struct remap;
template < typename T >
struct remap
<
T,
typename enable_if< is_same<T, unsigned char>::value
|| is_same<T, signed char>::value, T >::type
>
{
void foo(int);
};
int main()
{
remap<signed char> s;
s.foo(42);
}
Another possibility is to specialize a class template for categories of types (type traits):
#include <cstddef>
template < typename T >
struct is_integer
{
static const bool value = false;
};
template<> struct is_integer<signed char> { static const bool value = true; };
template<> struct is_integer<unsigned char> { static const bool value = true; };
template < typename T, typename dummy = T, std::size_t S = sizeof(T) >
struct remap;
template < typename T >
struct remap
<
T
, typename enable_if<is_integer<T>::value, T>::type
, 1 // assuming your byte has 8 bits
>
{
void foo(int);
};
int main()
{
remap<signed char> s;
s.foo(42);
}
You need your remap
trait to simply map from input types to output types, and have your foo<T>(int)
interface function delegate to a foo_implementation<remap<T>::type>(int)
implementation. i.e.:
template <typename T>
struct remap {
// Default: Output type is the same as input type.
typedef T type;
};
template <>
struct remap<char> {
typedef unsigned char type;
};
template <>
struct remap<signed char> {
typedef unsigned char type;
};
template <typename T>
void foo_impl(int x);
template <>
void foo_impl<unsigned char>(int x) {
std::cout << "foo_impl<unsigned char>(" << x << ") called\n";
}
template <typename T>
void foo(int x) {
foo_impl<typename remap<T>::type>(x);
}
See it live at ideone.com.
That said, it might be realistically simpler to define foo_char
, foo_int
and foo_short
and just call the correct one from client code. foo<X>()
isn't syntactically much different from foo_X()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With