2017-08-04 134 views
6

假設結合我有一個類可變參數模板默認模板參數

enum CallbackType 
{ 
    SYNC, 
    ASYNC 
} 


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

我希望能夠有選擇地specificy回調類型,同時仍然向阿不來提具有可變參數模板參數。現在我明白了編譯器不能區分它們,但是也許有辦法處理第一個模板參數是CallbackType的特定情況?

Callback<int int> //Should be Callback<SYNC, int, int> 
Callback<ASYNC, int, int> //Should be Callback<ASYNC, int, int> 

回答

7

有C++的兩個方面,當涉及到可變參數模板,這是相互衝突的,你的情況:

  1. 默認模板參數不應該先於非默認的模板參數。

  2. 變量模板參數不應該放在非變量模板參數之前。

這當然是可能的,在許多情況下,正確地申報和使用,模板,其參數不遵循這些規則,但這些情況是不是這個問題的目的很重要的。在你的情況下,最重要的是你的兩個模板參數都希望成爲模板中的最後一個參數,這是出於他們自己的理由。簡而言之,這就是問題所在。

解決這個矛盾最簡單的方法是使用內模板:

template<CallbackType CB = ASYNC> 
class CallbackClass { 

public: 

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

那麼,你的兩個例子變成:

CallbackClass<>::Callback<int, int> 

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

你當然,最終會得到更長的名字。但這就是typedefusing的用途。例如:

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

然後用

DefaultCallback<int, int> 
+0

太糟糕了,我們不能有好的東西:(我希望它總是把CallbackType作爲一個枚舉模板參數,其餘的作爲可變參數,但我geuss它並沒有把事情分開。 –

2

你可以得到一個語法非常接近原來的語法帶着幾分元編程。你會定義CallbackTypeCallbackImpl

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

我認爲這很明顯,爲什麼這通常不會完成。