Variadic template combined with default template argument

Suppose I have a class

enum CallbackType
{
    SYNC,
    ASYNC
}


template<CallbackType CB = SYNC, typename... Args>
class Callback
{
}

I would like to be able to optionally specify a callback type while still retaining the Variadic template argument. Now I understand that the compiler cannot distinguish them from each other, but maybe there is a way to handle the specific case when the first argument of the template refers to CallbackType?

Callback<int int> //Should be Callback<SYNC, int, int>
Callback<ASYNC, int, int> //Should be Callback<ASYNC, int, int>
+6
source share
2 answers

There are two aspects to C ++ when it comes to variable patterns that conflict with each other in your case:

  • Default template parameters should not precede template parameters without default.

  • Variadic .

, , , , . , , , . , .

- :

template<CallbackType CB = ASYNC>
class CallbackClass {

public:

    template<typename... Args> class Callback
    {
    }
};

:

CallbackClass<>::Callback<int, int>

CallbackClass<ASYNC>::Callback<int, int>

. typedef using. :

template<typename ...Args>
using DefaultCallback=CallbackClass<>::Callback<Args...>;

DefaultCallback<int, int>
+7

. CallbackType CallbackImpl:

enum CallbackType
{
    SYNC,
    ASYNC,
};

template<CallbackType CB, typename... Args>
class CallbackImpl
{
};

, " ":

// We need to treat the CallbackType argument as a type, not as a value.
// Thus, we need to wrap it in a type.
template <CallbackType cb>
using CallbackT = std::integral_constant<CallbackType, cb>;

// We need to be able to detect if the first type passed in is this CallbackT
template <typename T>
struct is_callback_type
    : std::false_type
{};

template <CallbackType cb>
struct is_callback_type<CallbackT<cb>>
    : std::true_type
{};

template <typename T>
using is_callback_type_t = typename is_callback_type<T>::type;

// Here we do the work. This is the base case, where the first arg
// is not a CallbackT. Note that this works for an empty Args as well
template <typename AlwaysVoid, typename... Args>
struct construct_callback_impl
{
    using type = CallbackImpl<SYNC, Args...>;
};

// If the Args list is of at least size 1,
template <typename CallbackType, typename... Args>
struct construct_callback_impl<
    // Use this specialization only if the first type is our CallbackT
    typename std::enable_if<is_callback_type_t<CallbackType>::value>::type,
    CallbackType,
    Args...>
{
    // Forward the specified CallbackType on to the CallbackImpl
    using type = CallbackImpl<CallbackType::value, Args...>;
};

// Wrap this utility into a nicer calling syntax    
template <typename... Args>
using Callback = typename construct_callback_impl<void, Args...>::type;

:

Callback<int, int>                   // type is CallbackImpl<SYNC, int, int>
Callback<CallbackT<SYNC>, int, int>  // type is CallbackImpl<SYNC, int, int>
Callback<CallbackT<ASYNC>, int, int> // type is CallbackImpl<ASYNC, int, int>
Callback<>                           // type is CallbackImpl<SYNC>

Live on Godbolt

, , .

+2

All Articles