0

Suppose I have an EventHandle class which is uniquely enumed by READ, WRITE, and SIGNAL, and I am implementing a member template which should return different data type with respect to different enums.

enum class EventType {
  READ,
  WRITE,
  SIGNAL
};

class EventHandle {

public:

  template <EventType type, typename = enable_if_t<is_same<type, READ>::value>>
  ReadEventHandle* cast () { return static_cast<ReadEventHandle>(this); }

  template <EventType type, typename = enable_if_t<is_same<type, WRITE>::value>>
  WriteEventHandle* cast () { return static_cast<WriteEventHandle>(this); }

  template <EventType type, typename = enable_if_t<is_same<type, SIGNAL>::value>>
  SignalEventHandle* cast () { return static_cast<SignalEventHandle>(this); }
};

And I have three derived classes from EventHandle.

class ReadEventHandle : public EventHandle {...}
class WriteEventHandle : public EventHandle {...}
class SignalEventHandle : public EventHandle {...}

Apprently what I wrote doesn't compile. Is there any way to achieve this type cast overload on "compile" time (e.g., no switch on enum)?

Jes
  • 2,614
  • 4
  • 25
  • 45

2 Answers2

1

I can't say I fully understand what you are trying to do, but you certainly are not using SFINAE correctly. Here is compilable code which I hope could serve as guidance:

#include <type_traits>

enum class EventType {
  READ,
  WRITE,
  SIGNAL
};

class ReadEventHandle { };
class WriteEventHandle { };
class SignalEventHandle { };

ReadEventHandle re;
WriteEventHandle we;
SignalEventHandle se;

class EventHandle {

public:

  template <EventType type,  std::enable_if_t<type == EventType::READ>* = nullptr >
  ReadEventHandle* cast () { return &re; }

  template <EventType type, std::enable_if_t<type == EventType::WRITE>* = nullptr >
  WriteEventHandle* cast () { return &we; }

  template <EventType type, std::enable_if_t<type == EventType::SIGNAL>* = nullptr>
  SignalEventHandle* cast () { return &se; }
};

void* check() {
  return EventHandle().cast<EventType::READ>(); // Depending on cast argument, different pointers returned
}
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Hi SergeyA, Thank you for the kind reply! This is exactly what I want! Could you please elaborate more on why your enable_if_t is actually working? Does it actually return a void type if the type matches? – Jes May 10 '16 at 20:38
  • @Jes, this is the essence of `enable_if`. When the condition is true, it has a type in it (which by default is `void`, but you can set it to something else in second argument to `enable_if`). When condition is false, there is no `type` member in it, and you have a substitution failure. `enable_if_t` is simply a template alias, which is the same thing as `typename std::enable_if<>::type` - but a very useful shorthand. – SergeyA May 10 '16 at 20:44
1

An alternative with some traits and specialization:

enum class EventType {
  READ,
  WRITE,
  SIGNAL
};

class ReadEventHandle;
class WriteEventHandle;
class SignalEventHandle;

template <EventType E> struct EventHandleType;

template <> struct EventHandleType<EventType::READ> { using type = ReadEventHandle; };
template <> struct EventHandleType<EventType::WRITE> { using type = WriteEventHandle; };
template <> struct EventHandleType<EventType::SIGNAL> { using type = SignalEventHandle; };

And then:

class EventHandle {
public:

  template <EventType E>
  typename EventHandleType<E>::type* cast();
};

template <EventType E>
typename EventHandleType<E>::type*
EventHandle::cast() { return static_cast<typename EventHandleType<E>::type*>(this); }

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302