Search code examples
c++templatesvariadic-templates

Sum type in variadic template


I have a template function defined for variadic templates of two kinds:

template<typename ...A>
void foo(std::shared_future<A>... args) {
    // ...
}

template<typename ...A>
void foo(std::future<A>&... args) {
    // ...
}

This works for foo(a, b, c) if a, b and c are either all futures, or all shared futures. How can I adapt this code to work for any mix of both types templates, i.e. both shared and raw futures in this case?


Solution

  • You can first accept arbitrary types Futures... and then use SFINAE to restrict them to std::[shared_]future:

    template<class>
    struct is_future_or_shared_future : std::false_type {};
    
    template<class T>
    struct is_future_or_shared_future<std::future<T>> : std::true_type {};
    
    template<class T>
    struct is_future_or_shared_future<std::shared_future<T>> : std::true_type {};
    
    template<class... Futures, typename = std::enable_if_t<
        std::conjunction_v<is_future_or_shared_future<Futures>...>>>
    void foo(Futures&... args) {
        // ...
    }
    

    If you want to take both lvalues and rvalues, use forwarding references:

    template<class T>
    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
    
    template<class... Futures, typename = std::enable_if_t<         
        std::conjunction_v<is_future_or_shared_future<remove_cvref_t<Futures>>...>>>
    void foo(Futures&&... args) {
        // ...
    }
    

    To access the inner type you can use a traits class:

    template<class>
    struct inner_type;
    
    template<class T>
    struct inner_type<std::future<T>> { 
        using type = T;
    };
    
    template<class T>
    struct inner_type<std::shared_future<T>> {
        using type = T;
    };
    

    and then write:

    typename B = std::result_of_t<
        F(typename inner_type<remove_cvref_t<Futures>>::type...)>;