2

I've been looking for solution of my issue over Stack Overflow and saw many similar topics, but there were no answers pointing to so specific case.

In the attached code, everything works unless I uncomment #define ISSUE. Then I'm getting many errors.

The goal here is to be able to only declare specializations in the class body. The implementation needs to be put in the same file after class definition.

Why this is not working? How to make it work?

My compiler supports C++17, nothing newer.

#include <cstdint>
#include <iostream>
#include <type_traits>

// #define ISSUE

template <typename T>
class Test
{
  public:
    template<typename U = T,
        std::enable_if_t<
            std::is_same<T,     bool>::value ||
            std::is_same<T,   int8_t>::value ||
            std::is_same<T,  uint8_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 1U))
        ,int> = 0>
    static void special(T data);

#if defined(ISSUE)
    template<typename U = T,
        std::enable_if_t<
            std::is_same<T,  int32_t>::value ||
            std::is_same<T, uint32_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 4U))
        ,int> = 0>
    static void special(T data);
#endif    
};

template <typename T>
template <typename, 
        std::enable_if_t<
            std::is_same<T,     bool>::value ||
            std::is_same<T,   int8_t>::value ||
            std::is_same<T,  uint8_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 1U))
        ,int>>
void Test<T>::special(T data)
{
    std::cout << "print 8-bit\n";
}

#if defined(ISSUE)
template <typename T>
template <typename, 
        std::enable_if_t<
            std::is_same<T,  int32_t>::value ||
            std::is_same<T, uint32_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 4U))
        ,int>>
void Test<T>::special(T data)
{
    std::cout << "print 32-bit\n";
}
#endif    

int main()
{
    Test<uint8_t>{}.special(5);
    Test<int8_t>{}.special(5);
    Test<bool>{}.special(true);
#if defined(ISSUE)
    Test<uint32_t>{}.special(5);
    Test<int32_t>{}.special(5);
#endif    
}

Only the post:

How to use std::enable_if on method of templated class with seperate declaration and definition via specialization

helped a bit to get what works with #define ISSUE commented out.

JeJo
  • 30,635
  • 6
  • 49
  • 88
Simon
  • 123
  • 3
  • There are no partial specializations in your code, only overloads. – user17732522 Mar 24 '23 at 12:23
  • This is not what specialization means. This is overloading. Template parameter definitions don't participate in overload resolution. That's it. – Sam Varshavchik Mar 24 '23 at 12:23
  • You used the `typename U = T` idiom partially for some reason. The point of it is to then only use `U` in the `enable_if` conditions. That way you make the `enable_if` conditions dependent on the template parameter of the member function template, which is necessary for SFINAE to apply. – user17732522 Mar 24 '23 at 12:28
  • So it works as ecpected when functions are defined inside class body. I need to make common specializations based on type sizes, and exclude doubles or classes... There is more different member functions, that needs other combinations of specializations and exclusions. The example code is just a top of the iceberg ;) Unfortunately cannot say anything more about implementation. That's why example code may look a bit too simple. – Simon Mar 24 '23 at 13:47

1 Answers1

3

Why this is not working?

Firstly, they are just template function overloads, not a specialization of member functions.

The SFINAE can only work, if std::enable_if_t's condition depends up on the function's template parameter type. That means, in your shown code, it must depend upon U, rather than with class template parameter T.

In addtion to that, the second parameter of std::enable_if_t is the return type of the function which will be SFINAEd. You have provided int as return, which must be void (or do not provide, since it is the default type for std::enable_if_t).


How to make it work?

I propose the alternative approach of using std::enable_if_t directly in the function signature as the return type, instead of inside a template parameter list.

You can still keep the functions' definition separately outside the class as well.


template <typename T>
class Test
{
public:
    template<typename U = T>
    std::enable_if_t<
        std::is_same<U, bool>::value ||
        std::is_same<U, int8_t>::value ||
        std::is_same<U, uint8_t>::value ||
        (std::is_enum<U>::value && (sizeof(U) == 1U))
    > special(T data);

    template<typename U = T>
    std::enable_if_t<
        std::is_same<U, int32_t>::value ||
        std::is_same<U, uint32_t>::value ||
        (std::is_enum<U>::value && (sizeof(U) == 4U))
    > special(T data);
};

See live demo godbolt.org


That being said, since you have access to C++17 compiler, have a look at if constexpr, which is a better alternative for the scenario. Or using fold expression you might also reduce the extended std::enable_if condition as simple as follows:

// alias std::enable_if for type checking
template <std::size_t N, typename U, typename... Ts>
using enabled_types = std::enable_if_t<
    (std::is_same_v<U, Ts> || ...) ||
    (std::is_enum_v<U> && (sizeof(U) == N))
 >;

template <typename T>
class Test
{
public:
    template<typename U = T>
    enabled_types<1U, U, bool, int8_t, uint8_t> special(T data);

    template<typename U = T>
    enabled_types<4U, U, int32_t, uint32_t> special(T data);
};

See live demo godbolt.org

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Thank you for it. Looks promising but returned void type is missing. Actually I need to be able to return void, enum, another class... generally speaking any type but T type. – Simon Mar 26 '23 at 12:26
  • That's not it. Let's say the class `Result` is defined somewhere. I need each overload of `special()` to return `Result` no matter what was `T` type. And `Result` is not enabled type for `Test` class. – Simon Mar 27 '23 at 07:39
  • Oh no, sorry. Looks like it's OK. Let me check. – Simon Mar 27 '23 at 07:44