I have code like this:
template< class T >
struct Value
{
/* quite a lot of other functions which I do not want to specialize, too */
void print( void );
};
template<> void Value< short int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< long int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned short int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned long int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< float >::print() { std::cout << "is floating point" << std::endl; }
template<> void Value< double >::print() { std::cout << "is floating point" << std::endl; }
template<> void Value< long double >::print() { std::cout << "is floating point" << std::endl; }
template< class T > void Value<T>::print() { std::cout << "unsupported type" << std::endl; }
int main( void )
{
Value< float >().print();
Value< double >().print();
Value< short >().print();
Value< char >().print();
}
Output:
is floating point
is floating point
is integral
unsupported type
I want to change this in order to reduce code duplication, especially, because the code body is much longer than a simple std::cout
. To illustrate the direction I want to go, the simplest idea coming to mind would be to use macros:
#define TMP(T) \
template<> void Value<T>::print() { std::cout << "is integral" << std::endl; }
TMP( short int )
TMP( int )
TMP( long int )
TMP( unsigned short int )
TMP( unsigned int )
TMP( unsigned long int )
#undef TMP
#define TMP(T) \
template<> void Value<T>::print() { std::cout << "is floating point" << std::endl; }
TMP( float )
TMP( double )
TMP( long double )
#undef TMP
But I want to get it to work using C++11 template magic. I already tried using std::enable_if
, but I just can't get it to work. E.g. this
template< class T >
void Value<
typename std::enable_if< std::is_integral<T>::value, T >::type
>::print( void )
{
std::cout << "is integral" << std::endl;;
}
gives me
test.cpp:26:24: error: invalid use of incomplete type ‘struct Value<typename std::enable_if<std::is_integral<_Tp>::value, T>::type>’
>::type >::print( void )
^
test.cpp:14:8: error: declaration of ‘struct Value<typename std::enable_if<std::is_integral<_Tp>::value, T>::type>’
struct Value
and using std::enable_if
on the return type:
template< class T >
typename std::enable_if< std::is_integral<T>::value, void >::type
Value<T>::print( void )
{
std::cout << "is integral" << std::endl;;
}
gives me:
test.cpp:41:1: error: prototype for ‘typename std::enable_if<std::is_integral<_Tp>::value, void>::type Value<T>::print()’ does not match any in class ‘Value<T>’
Value<T>::print( void )
^
test.cpp:16:17: error: candidate is: static void Value<T>::print()
static void print( void );
Of course there are many similar questions already:
- http://www.cplusplus.com/forum/beginner/151879/
- match multiple types for template specialization resolution
- Template specialization for multiple types
- How to do one explicit specialization for multiple types?
But they often are about simple functions, not methods of templated classes. Also they often don't strictly separate declaration and definition as I want to.
This questions sounds very similar, but it specializes the method not in respect to the struct/class template argument, but in respect to another template argument.
I just can't seem to apply the answers to my specific problem, because of the aforementioned errors. Also I don't want to specialize the whole class, because the class itself shares many methods which are identical for all types T. I don't want to trade one copy-paste code for another.
Bonus for explaining why the two error messages happen. What rule am I breaking. To me it seems that ...::type
isn't substituted at all with T
or void
.