9

I'm working on a project which has an template function as so:

template <class T> 
T foo<T>(T val) { return someFunc(val); }

template <>
bool foo<bool>(bool val) { return otherFunc(val); };

Now, I have a class Bar, which I don't want to accept as input. In fact, I want it to generate an easy to spot compile error. The problem is that if I do this:

template <>
Bar foo<Bar>(Bar val) { static_assert(false,"uh oh..."); }

It fails on every compile. I found https://stackoverflow.com/a/3926854/7673414, which says that I need to make reference to the template type, otherwise the static assert always takes place. The problem is I don't have a template type here. If I do:

template< typename T >
struct always_false { 
    enum { value = false };  
};

template <>
Bar foo<Bar>(Bar val) { static_assert(always_false<Bar>::value,"uh oh..."); }

then it also always fails compiling. Is there a way to ensure that an instantiation of the template with type Bar always causes a compile error?

max66
  • 65,235
  • 10
  • 71
  • 111
blackghost
  • 1,730
  • 11
  • 24

4 Answers4

23

Since foo is a complete specialization, it will always get compiled, and the static assert will always get called.

However, there’s an easier way:

template <>
Bar foo<Bar>(Bar val) = delete;

This will say that this specific version is deleted, and cannot be called.

Daniel H
  • 7,223
  • 2
  • 26
  • 41
  • That's nice but I'd delete the d-tor instead of having to delete all the various c-tors. – Yehezkel B. Aug 20 '17 at 03:03
  • 1
    @YehezkelB. This is a function template, not a class template. You can delete function template specializations, not just constructors and destructors. – Daniel H Aug 24 '17 at 21:32
3

Another way is enable the template (not specialized version) only if the type is different from Bar

template <class T> 
typename std::enable_if< ! std::is_same<T, Bar>::value, T>::type foo<T>(T val)
 { return someFunc(val); }

If you can use C++14, is can be simplified using std::enable_if_t

template <class T> 
std::enable_if_t< ! std::is_same<T, Bar>::value, T> foo<T>(T val)
 { return someFunc(val); }
max66
  • 65,235
  • 10
  • 71
  • 111
2

You can use std::is_same to help with your requirement.

template <class T> 
T foo<T>(T val)
{
   // Make sure T is not Bar
   static_assert(std::is_same<T, Bar>::value == false, "uh oh...");
   return someFunc(val);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

If you are using c++17, you can put everything together with constexpr if:

template< typename T >
auto foo( T val )
{
    static_assert( !std::is_same_v<T,Bar> );

    if constexpr( std::is_same_v<T,bool> )
    {
        return  other_func( val );
    }
    else
    {
        return some_func( val );
    }

}

Then you can static_assert at the first line, without the pain of compilation failure of the template specific instantiation.

A live example is available at https://wandbox.org/permlink/PpR6G0gcvMRoxhhZ

Feng Wang
  • 1,506
  • 15
  • 17