PolGraphic
PolGraphic

Reputation: 3364

Creating an template<T> only for such T that have operator/ defined

I would like to define an template<T> for type, but I have to be sure that only types that have operator/ and operator+ defined can be passed as T.

It's because I want to be able to get interpolated value for two of them (that are of the same type), e.g.:

template<class T>
class Key final : public KeyBase{
public: //to keep the example simple
    //unsigned timeMS; <-- iherited from KeyBase
    T value;

public:
    Key(unsigned timeMS, const T &value) 
        : KeyBase(timeMS), value(value){}
    T inline getValue(){ return value; }
};

Key<float> start = {10, 5.0f};
Key<float> end = {15, 3.0f};
float ratioAtTime12 = 12 / (end.timeMS - start.timeMS);
float valueAtTime12 = start.value + (end.value - start.value) * ratio;

//Point2D is my own custom type that have operator+, - and / defined
Key<Point2D> start2 = {10, Point2D(10, 15)};
Key<Point2D> end2 = {15, Point2D(111, 6)};
...

Key<Character> start3 = {10, Character("Alan")}; //SHOULD generate an error
//because my custom Character type has no operator+, - or / defined!

For simple types like float, int etc. it's ok. But how to prevent using complex types as T if they don't have operator/ and operator+ defined?

Upvotes: 3

Views: 96

Answers (1)

Columbo
Columbo

Reputation: 60999

If you want a nice error message that doesn't include douzens of operators found via ADL or the like, you can define traits:

template <typename, typename=void>
struct isAddable : std::false_type {};

template <typename T>
struct isAddable<T, decltype(void(std::declval<T>() + std::declval<T>()))>
    : std::true_type {};

template <typename, typename=void>
struct isDividable : std::false_type {};

template <typename T>
struct isDividable<T, decltype(void(std::declval<T>() / std::declval<T>()))>
    : std::true_type {};

... and use a static assertion.

static_assert( isAddable<T>{} && isDividable<T>{}, "Invalid T!" );

Or, to get more specific information:

static_assert( isAddable<T>{}, "T not addable!" );
static_assert( isDividable<T>{}, "T not dividable!" );

Demo.


You can also use a handy macro to define such traits.

#define VAL std::declval<T>()

#define DEF_TRAIT(name, expression)                                  \
template <typename, typename=void> struct name : std::false_type {}; \
template <typename T>                                                \
struct name<T, decltype(void(expression))> : std::true_type {};

DEF_TRAIT(isDividable, VAL / VAL)
DEF_TRAIT(isAddable, VAL + VAL)

Demo.

Upvotes: 6

Related Questions