0

I am banging my head around why the following leads to a compile error. I am a novice in meta programming but to my understanding of SFINAE principle the following functions are mutually exclusive hence it is not a 'redefinition' of an existing function.

#include <type_traits>

template<typename T>
using IsNotEnum = typename std::enable_if<!std::is_enum<T>::value>::type;

template<typename T>
using IsEnum = typename std::enable_if<std::is_enum<T>::value>::type;

template<typename T, typename = IsNotEnum<T>>
void doSomething()
{
}

template<typename T, typename = IsEnum<T>>
void doSomething()
{
}

g++ 7.5 complains with followings:

error: redefinition of ‘template<class T, class> void doSomething()’
void doSomething()
  ^~~~~~~~~~~
note: ‘template<class T, class> void doSomething()’ previously declared here
void doSomething()
  • 1
    See [common mistake with enable_if](https://stackoverflow.com/questions/38305222/default-template-argument-when-using-stdenable-if-as-templ-param-why-ok-wit/38305320#38305320) – Jarod42 Nov 20 '20 at 21:28
  • 1
    This reminds me of [this question](https://stackoverflow.com/questions/11055923/stdenable-if-parameter-vs-template-parameter/51659883#51659883) – JVApen Nov 20 '20 at 21:32
  • Default arguments are not part of the function signature. – super Nov 20 '20 at 22:16

1 Answers1

2

Try rewriting your function as follows

template<typename T, IsNotEnum<T> = nullptr>
void doSomething()
{
}

template<typename T, IsEnum<T> = nullptr>
void doSomething()
{
}

and your usings as follows

template <typename T>
using IsNotEnum = typename std::enable_if<!std::is_enum<T>::value, std::nullptr_t>::type;

template <typename T>
using IsEnum = typename std::enable_if<std::is_enum<T>::value, std::nullptr_t>::type;

or simply, if you have a C++14 or more recent compiler,

template <typename T>
using IsNotEnum = std::enable_if_t<!std::is_enum<T>::value, std::nullptr_t>;

template <typename T>
using IsEnum = std::enable_if_t<std::is_enum<T>::value, std::nullptr_t>;

The problem in your code is that you can't write two different functions differing only for a template default type/value.

So, removing the default value, SFINAE doens't disable the unwanted function. So you have a function collision.

Only removing the type (on the left of the equal sign) SFINAE remove the unwanted function.

max66
  • 65,235
  • 10
  • 71
  • 111
  • nice, I didn't realize the "typename =" is a default type assignment. With that piece of info it makes perfectly sense. Thanks – idle_roamer Nov 21 '20 at 21:28