简体   繁体   中英

Clean static interface with C++20 concepts

I was trying to create a static interface using C++20 concepts, and the following code seems to do the job:

template <class FOO>
concept FooConcept = requires {
    static_cast<void (FOO::*)(int)>(&FOO::operator());
    static_cast<void (FOO::*)(char)>(&FOO::operator());
};

In particular, FooConcept is satisfied by a class FOO if such class overloads operator() twice: with an int argument and a char argument, respectively.

This solution seems to work fine, but it doesn't look beautiful. Indeed, I would've liked much more the following forms:

template <class FOO>
concept FooConcept = requires (FOO foo, int i, char c) {
    { foo(i) } -> std::same_as<void>;
    { foo(c) } -> std::same_as<void>;
};

or

template <class FOO>
concept FooConcept = std::invocable<FOO, int> && std::invocable<FOO, char>;

However, these approaches don't work due to implicit conversions (see this post ). Is there a better, more "semantic" way to express this constraint?

With some helper, and with non- final classes, you might do:

template <typename T>
struct DeletedOperator : T
{
    using T::operator ();

    template <typename ... Ts>
    void operator()(Ts&&...) = delete;

    template <typename ... Ts>
    void operator()(Ts&&...) const = delete;
};

template <class FOO>
concept FooConcept = requires (DeletedOperator<FOO> foo, int i, char c) {
    { foo(i) } -> std::same_as<void>;
    { foo(c) } -> std::same_as<void>;
};

Demo

I think we can get rid of the non- final constraint by adjusting the helper.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM