Naios
Naios

Reputation: 1573

Enable class methods without SFINAE

I'm looking for a way to enable class methods without SFINAE, maybe through inheritance.

Im working on an improved version of std::function (functor class with operator()) which qualifier (const, volatile) is dependent on it's template argument for example:

and so on.

The main reason why i can't use SFINAE to solve this is that if i use SFINAE the operator() needs to be templated like:

template<typename R = ReturnType>
auto operator() (Args&&... args)
    -> std::enable_if_t<!Constant && !Volatile, R>
{
    return (*_impl)(std::forward<Args>(args)...);
}

template<typename R = ReturnType>
auto operator() (Args&&... args) const volatile
    -> std::enable_if_t<Constant && Volatile, R>
{
    return (*_impl)(std::forward<Args>(args)...);
}

which means the operator() isn't referenceable anymore through using:

&my_functor<void() const>::operator()

My first intention was to use superclasses where i can inherit the operator() methods from but the methods still need to have access to the main implementation which is contained by a std::unique_ptr which holds a virtual class, and i don't want to pass a reference of the std::unique_ptr to the superclass.

Is there another or better way to enable class methods without templating it (which excludes SFINAE).

Upvotes: 4

Views: 211

Answers (1)

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48487

operator() isn't referenceable anymore (...)

With the SFINAE-based approach the operator is still accessible, with syntax:

&my::functor<void() const>::operator()<>
//                                    ↑↑

My first intention was to use superclasses where i can inherit the operator() methods from but the methods still need to have access to the main implementation which is contained by a std::unique_ptr

You can use the CRTP idiom, so that impl is accessible by down-casting this:

template <typename /*Fn*/, bool /*NonCopyable*/, bool /*Constant*/, bool /*Volatile*/>
class function;

template <typename CRTP>
struct call_operator;

template <typename ReturnType, typename... Args, bool NonCopyable>
struct call_operator<function<ReturnType(Args...), NonCopyable, true, false>>
{
    using func = function<ReturnType(Args...), NonCopyable, true, false>;

    ReturnType operator()(Args&&... args) const
    {
        return (*static_cast<const func*>(this)->_impl)(std::forward<Args>(args)...);
    }
};

template <typename ReturnType, typename... Args, bool NonCopyable>
struct call_operator<function<ReturnType(Args...), NonCopyable, true, true>>
{
    using func = function<ReturnType(Args...), NonCopyable, true, true>;

    ReturnType operator()(Args&&... args) const volatile
    {
        return (*static_cast<const volatile func*>(this)->_impl)(std::forward<Args>(args)...);
    }
};

template<typename ReturnType, typename... Args, bool NonCopyable, bool Constant, bool Volatile>
class function<ReturnType(Args...), NonCopyable, Constant, Volatile> : call_operator<function<ReturnType(Args...), NonCopyable, Constant, Volatile>>
{
    friend struct call_operator<function<ReturnType(Args...), NonCopyable, Constant, Volatile>>;

    std::unique_ptr<wrapper_impl<ReturnType(Args...)>> _impl;

public:
    function()
        : _impl(new fake_wrapper_impl<ReturnType(Args...)>()) { }

    using call_operator<function<ReturnType(Args...), NonCopyable, Constant, Volatile>>::operator();    
};

DEMO

Upvotes: 4

Related Questions