Search code examples
c++c++11templatesvariadic-templatesresult-of

c++ how to define a std::result_of<F(R)> that can handle R is void


When i am using

std::result_of<F(R)>

like this:

template<typename R, typename... Args>
class Task
{
    //...

    template<typename F>
    auto Then(F&& f) -> Task<typename std::result_of<F(R)>::type(Args...)>
    {  
        //... 
    }    
}; 

Since R is another function's output type, so R may be void, in this situation, there will be:

error: invalid parameter type ‘void’.

So the question is how to handle both R is void and is not?


Solution

  • Option #1

    SFINAE-based:

    template <typename F>
    class Task;
    
    template <typename R, typename... Args>
    class Task<R(Args...)>
    {
    public:
        template <typename F, typename Ret = R>
        auto Then(F&& f)
            -> typename std::enable_if<!std::is_void<Ret>::value, Task<typename std::result_of<F(Ret)>::type(Args...)>>::type
        {
            return {};
        }
    
        template <typename F, typename Ret = R>
        auto Then(F&& f)
            -> typename std::enable_if<std::is_void<Ret>::value, Task<typename std::result_of<F()>::type(Args...)>>::type
        {
            return {};
        }
    };
    

    DEMO

    Option #2

    Partial-specialization of the class template:

    template <typename F>
    class Task;
    
    template <typename R, typename... Args>
    class Task<R(Args...)>
    {
    public:
        template <typename F>
        auto Then(F&& f)
            -> Task<typename std::result_of<F(R)>::type(Args...)>
        {
            return {};
        }
    };
    
    template <typename... Args>
    class Task<void(Args...)>
    {
    public:
        template <typename F>
        auto Then(F&& f)
            -> Task<typename std::result_of<F()>::type(Args...)>
        {
            return {};
        }
    };
    

    DEMO 2

    Option #3

    Tag-dispatching:

    template <typename F>
    class Task;
    
    template <typename R, typename... Args>
    class Task<R(Args...)>
    {    
    private:
        template <typename F>
        auto ThenImpl(F&& f, std::true_type)
            -> Task<typename std::result_of<F()>::type(Args...)>
        {
            return {};
        }
    
        template <typename F, typename Ret = R>
        auto ThenImpl(F&& f, std::false_type)
            -> Task<typename std::result_of<F(Ret)>::type(Args...)>
        {
            return {};
        }
    
    public:
        template <typename F>
        auto Then(F&& f)
            -> decltype(ThenImpl(std::forward<F>(f), std::is_void<R>{}))
        {
            return ThenImpl(std::forward<F>(f), std::is_void<R>{});
        }
    };
    

    DEMO 3