user3600124
user3600124

Reputation: 883

How do I declare SFINAE class?

Something is not working quite well for me. Is this the way to declare a class, that accepts only floating point template parameter?

template <typename T, swift::enable_if<std::is_floating_point<T>::value> = nullptr>
  class my_float;

I fail to define methods outside this class. Doesn't compile, not sure why

Upvotes: 5

Views: 3024

Answers (3)

SU3
SU3

Reputation: 5387

You can also use static_assert to poison invalid types.

template <typename T>
class my_float {
  static_assert(std::is_floating_point<T>::value,
    "T is not a floating point type");
  // . . .
};

It's a little bit more direct, in my opinion.

With either of the other approaches, e.g.

template <typename T, bool = std::is_floating_point<T>::value>
class my_float;

template <typename T> class my_float<T, true> { /* . . . */ };

my_float<int,true> is a valid expression. I'm not saying that that's a bad approach, but if you want to avoid this, you'll have to encapsulate my_float<typename,bool> within another template, to avoid exposing the bool template parameter.

Upvotes: 2

Marine Galantin
Marine Galantin

Reputation: 2279

indeed, something like this worked for me (thanks to SU3's answer).

template<typename T, bool B = false>
struct enable_if {};

template<typename T>
struct enable_if<T, true> {
    static const bool value = true;
};

template<typename T, bool b = enable_if<T,is_allowed<T>::value>::value >
class Timer{ void start(); };

template<typename T, bool b>
void Timer<T,b>::start()
{ \* *** \*}

I am posting this answer because I did not want to use partial specialization, but only define the behavior of the class outside.

a complete workable example:

typedef std::integral_constant<bool, true> true_type;
typedef std::integral_constant<bool, false> false_type;

struct Time_unit {
};

struct time_unit_seconds : public Time_unit {
    using type = std::chrono::seconds;
};

struct time_unit_micro : public Time_unit {
    using type = std::chrono::microseconds;
};

template<typename T, bool B = false>
struct enable_if {
};

template<typename T>
struct enable_if<T, true> {
    const static bool value = true;
};

template<typename T,
        bool b = enable_if<T,
                std::is_base_of<Time_unit,
                        T>::value
        >::value>
struct Timer {
    int start();
};

template<typename T, bool b>
int Timer<T, b>::start() { return 1; }

int main() {
    Timer<time_unit_seconds> t;
    Timer<time_unit_micro> t2;
//    Timer<double> t3; does not work !

    return 0;
}

Upvotes: 0

max66
max66

Reputation: 66200

Well... not exactly SFINAE... but maybe, using template specialization? Something as follows ?

template <typename T, bool = std::is_floating_point<T>::value>
class my_float;

template <typename T>
class my_float<T, true>
 {
   // ...
 };

If you really want use SFINAE, you can write

template <typename T,
   typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
class my_float
 {
   // ...
 };

or also (observe the pointer there isn't in your example)

template <typename T,
   typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
class my_float  // ------------------------------------------------^
 {
 };

-- EDIT --

As suggested by Yakk (thanks!), you can mix SFINAE and template specialization to develop different version of your class for different groups of types.

By example, the following my_class

template <typename T, typename = void>
class my_class;

template <typename T>
class my_class<T,
   typename std::enable_if<std::is_floating_point<T>::value>::type>
 {
   // ...
 };

template <typename T>
class my_class<T,
   typename std::enable_if<std::is_integral<T>::value>::type>
 {
   // ...
 };

is developed for in two versions (two different partial specializations), the first one for floating point types, the second one for integral types. And can be easily extended.

Upvotes: 1

Related Questions