Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define C++ concept to describe the member function template

For example: define a C++ concept with the following constraint: the type should have a function template called f that receive an integer value as the parameter.

Here is a valid type:

struct A
{
    template <std::integral T>
    void f();
};

when defining the corresponding concept, I can write:

template <typename T>
concept HasValidF = requires(T t) {
    t.f<int>(0);
};

But this one cannot cover all other integral types. A modification might be

template <typename T>
concept HasValidF = requires(T t) {
    t.f<int>(0);
    t.f<long>(0);
    t.f<unsigned>(0);
    ...
};

But the above implementation is not ideal. Are there anyway to avoid such enumeration? Thanks!

like image 332
Wei Li Avatar asked Oct 14 '25 03:10

Wei Li


1 Answers

I'd create a helper. One of these may suite you:

template <class T, class... Ts>
concept HasOverloadForOneOf =
    (... || requires { std::declval<T>().template f<Ts>(); });

template <class T, class... Ts>
concept HasOverloadForAllOf =
    (... && requires { std::declval<T>().template f<Ts>(); });

Then defining HasValidF becomes a little simpler although you still have to list all the types that should be taken into consideration. If you want your type to have at least one matching overload, you'd use HasOverloadForOneOf. If you want it to have overloads for all, use HasOverloadForAllOf:

template <class T>
concept HasValidF =
    HasOverloadForAllOf<T, bool, char, signed char, unsigned char, short,
                        unsigned short, int, unsigned, long, unsigned long,
                        long long, unsigned long long>;

Demo


If you want all const, volatile and const volatile versions too, you could either list them manually in HasOverloadForOneOf/HasOverloadForAllOf or add helpers to create them:

template <class...> struct make_const;
template <class... Ts>
struct make_const<std::tuple<Ts...>> {
    using type = std::tuple<std::add_const_t<Ts>...>;
};
template <class T> using make_const_t = make_const<T>::type;

template <class...> struct make_volatile;
template <class... Ts>
struct make_volatile<std::tuple<Ts...>> {
    using type = std::tuple<std::add_volatile_t<Ts>...>;
};
template <class T> using make_volatile_t = make_volatile<T>::type;
//------------------------------------------------------------------------------
using base_integrals = std::tuple<bool, char, signed char, unsigned char, short,
                                  unsigned short, int, unsigned, long,
                                  unsigned long, long long, unsigned long long>;
using all_integrals =
    decltype(std::tuple_cat(base_integrals{},
                            make_const_t<base_integrals>{},
                            make_volatile_t<base_integrals>{},
                            make_const_t<make_volatile_t<base_integrals>>{}));
//------------------------------------------------------------------------------
template<class...> struct HasValidFHelper;
template<class T, class... Ts> struct HasValidFHelper<T, std::tuple<Ts...>> {
    static constexpr bool value = HasOverloadForAllOf<T, Ts...>;
};
template <class T>
concept HasValidF = HasValidFHelper<T, all_integrals>::value;

Demo

like image 180
Ted Lyngmo Avatar answered Oct 19 '25 14:10

Ted Lyngmo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!