Template constraints

Three macros are provided to help improve the readability of template constraints.

TICK_REQUIRES

The TICK_REQUIRES can be used on template parameters. For example,

template<class T, TICK_REQUIRES(is_incrementable<T>())>
void increment(T& x)
{
    x++;
}

TICK_CLASS_REQUIRES

The TICK_CLASS_REQUIRES can be used when template specialization is done on classes. For example,

template<class T, class=void>
struct foo
{
    ...
};

template<class T>
struct foo<T, TICK_CLASS_REQUIRES(is_incrementable<T>() and not std::is_integral<T>())>
{
    ...
};

template<class T>
struct foo<T, TICK_CLASS_REQUIRES(std::is_integral<T>())>
{
    ...
};

TICK_MEMBER_REQUIRES

The TICK_MEMBER_REQUIRES can be used for member function inside of classes, that are not templated. For example,

template<class T>
struct foo
{
    T x;

    TICK_MEMBER_REQUIRES(is_incrementable<T>())
    void up()
    {
        x++;
    }
};

TICK_PARAM_REQUIRES

The TICK_PARAM_REQUIRES can be used in the paramater of the function. This is useful for lambdas:

auto increment = [](auto& x, TICK_PARAM_REQUIRES(is_incrementable<decltype(x)>()))
{
    x++;
};

Also, the trait function is provided which can be used to deduce the type of the parameters:

auto increment = [](auto& x, TICK_PARAM_REQUIRES(trait<is_incrementable>(x)))
{
    x++;
};

Note: The trait function always deduces the type without references. So trait<std::is_lvalue_reference>(x) will always be false.

TICK_FUNCTION_REQUIRES

The TICK_FUNCTION_REQUIRES can be used on functions. This requires placing parenthesis around the return type:

template<class T>
TICK_FUNCTION_REQUIRES(is_incrementable<T>())
(void) increment(T& x)
{
    x++;
}

Note: The TICK_REQUIRES should be preferred.