10

I would like to define a template function but disallow instantiation with a particular type. Note that in general all types are allowed and the generic template works, I just want to disallow using a few specific types.

For example, in the code below I wish to prevent using double with the template. This doesn't actually prevent the instantiation, but just causes a linker error by not having the function defined.

template<typename T>
T convert( char const * in )
{ return T(); }

//this way creates a linker error
template<>
double convert<double>( char const * in );

int main()
{
    char const * str = "1234";

    int a = convert<int>( str );
    double b = convert<double>( str );
}

The code is just a demonstration, obviously the convert function must do something more.

Question: In the above code how can I produce a compiler error when trying to use the convert<double> instantiation?


The closest related question I can find is How to intentionally cause a compile-time error on template instantiation It deals with a class, not a function.

The reason I need to do this is because the types I wish to block will actually compile and do something with the generic version. That's however not supposed to be part of the contract of the function and may not be supported on all platforms/compilers and in future versions. Thus I'd like to prevent using it at all.

Community
  • 1
  • 1
edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267

4 Answers4

3

I would use a static assert within your function call to create the proper failure during function instantiation:

template<typename T>
class is_double{ static const int value = false; }

template<>
class is_double<double>{ static const int value = true; }

template<typename T>
T convert( const char *argument ){
    BOOST_STATIC_ASSERT( !is_double<T>::value );
    //rest of code
}

And that should work within a function.

wheaties
  • 35,646
  • 15
  • 94
  • 131
1

You could use a functor instead of a function:

template<typename T>
struct convert { 
    T operator()(char const * in) const { return T(); } 
};
template<> struct convert<double>;

int main()
{
    char const * str = "1234";

    int a = convert<int>()( str );
    double b = convert<double>()( str ); // error in this line

    return 0;
}

This will give you an error at the point of instantiation.

By adding helper function you will get the wanted behaviour:

template<typename T>
struct convert_helper { 
    T operator()(char const * in) const { return T(); } 
};
template<> struct convert_helper<double>;

template<typename T>
T convert( char const * in ) { return convert_helper<T>()( in ); }

int main()
{
    char const * str = "1234";

    int a = convert<int>( str );
    double b = convert<double>( str );

    return 0;
}
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
1

If you don't want to rely on static_assert or make the code portable pre-C++0x, use this:

template<class T>
void func(){
    typedef char ERROR_in_the_matrix[std::is_same<T,double>::value? -1 : 1];
}

int main(){
  func<int>(); // no error
  func<double>(); // error: negative subscript
}
Xeo
  • 129,499
  • 52
  • 291
  • 397
0

Consider Boost disable_if and Boost TypeTraits

Take a look at How can I write a function template for all types with a particular type trait?

This is an example:

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

template<typename T>
T convert( char const * in, 
           typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0 )
{ return T(); }


int main()
{
    char const * str = "1234";

    int a = convert<int>( str );
    double b = convert<double>( str );
    return 0;
}


This is the compilation error for the string

double b = convert<double>( str );

1>.\simple_no_stlport.cpp(14) : error C2770: invalid explicit template argument(s) for 'T convert(const char *,boost::disable_if,T>::type *)' 1> .\simple_no_stlport.cpp(5) : see declaration of 'convert'

Community
  • 1
  • 1