2017-08-04 24 views
6

Giả sử tôi có một lớpvariadic mẫu kết hợp với mẫu mặc định luận

enum CallbackType 
{ 
    SYNC, 
    ASYNC 
} 


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

Tôi muốn để có thể tùy chọn specificy loại callback trong khi vẫn đang được cá ở nước ngọt có mẫu đối số variadic. Bây giờ tôi hiểu rằng trình biên dịch không thể phân biệt chúng, nhưng có thể có một số cách để xử lý trường hợp cụ thể trong đó đối số mẫu đầu tiên là của CallbackType?

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

Trả lời

7

Có hai khía cạnh của C++, khi nói đến các mẫu variadic, đó là mâu thuẫn với nhau trong trường hợp của bạn:

  1. thông số mẫu được Mặc không nên đặt trước các thông số mẫu phi defaulted .

  2. Thông số mẫu biến thể không được đặt trước các tham số mẫu không phải là biến thể.

Chắc chắn có thể, trong nhiều trường hợp, phải khai báo và sử dụng đúng, các mẫu có tham số không tuân theo các quy tắc này, nhưng những trường hợp này không quan trọng cho mục đích của câu hỏi này. Trong trường hợp của bạn, những gì nó đi xuống là cả hai tham số mẫu của bạn muốn là tham số cuối cùng trong mẫu của chúng, vì lý do riêng của chúng. Đó là vấn đề, trong một nutshell.

Cách đơn giản nhất để giải quyết cuộc xung đột này là sử dụng một mẫu nội:

template<CallbackType CB = ASYNC> 
class CallbackClass { 

public: 

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

Sau đó, hai ví dụ của bạn trở thành:

CallbackClass<>::Callback<int, int> 

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

Bạn sẽ kết thúc với tên lớp dài hơn, tất nhiên. Nhưng đó là những gì typedefusing là dành cho. Ví dụ:

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

sau đó sử dụng

DefaultCallback<int, int> 
+0

Quá tệ, chúng tôi không thể có những điều tốt đẹp :(, tôi muốn nó luôn luôn gọi CallbackType như một đối số mẫu enum và phần còn lại như variadic, nhưng tôi geuss nó không nói những điều ngoài như vậy. –

2

Bạn có thể nhận được một cú pháp rất gần với cú pháp ban đầu của bạn với một chút lập trình meta. Bạn muốn xác định bạn CallbackTypeCallbackImpl:

enum CallbackType 
{ 
    SYNC, 
    ASYNC, 
}; 

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

Sau đó làm một số điều để có được "đối số mặc định":

// 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; 

Sau đó, nó có thể được sử dụng:

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

Tôi nghĩ rằng nó khá rõ ràng tại sao điều này thường không được thực hiện.

Các vấn đề liên quan