2015-04-16 14 views
11

Vì có giới hạn trên allowed non-type variadic templates, tôi đang cố gắng viết một hàm lấy số lượng tùy ý tăng gấp đôi sử dụng enable_if. Về bản chất, tôi muốn làm một cái gì đó như:Kiểm tra loại gói tham số bằng cách sử dụng enable_if

template<typename... T, 
    typename = typename std::enable_if<std::is_convertible<T, double>::value, T>::type> 
    foo(T... t){ /* code here */ } 

tôi chọn để đặt enable_if như một giá trị mặc định cho một tham số giấu tên kể từ khi chức năng của tôi thực sự là một nhà xây dựng và sẽ không có một giá trị trả về. Điều này sẽ làm việc cho một tham số duy nhất, nhưng vì nó là một mẫu variadic T là một gói tham số, và mã trên không hợp lệ. Vì vậy, làm thế nào tôi có thể kiểm tra mọi tham số có thể chuyển đổi thành một đôi?

Trả lời

17

Thủ thuật bool_pack lần nữa.

template<bool...> struct bool_pack; 
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>; 

Sau đó

template<class R, class... Ts> 
using are_all_convertible = all_true<std::is_convertible<Ts, R>::value...>; 

và cuối cùng

template<typename... T, 
typename = typename enable_if<are_all_convertible<double, T...>::value>::type> 
foo(T... t){ /* code here */} 
+0

Gọn gàng, nhưng không có khái quát hóa rõ ràng về "bất kỳ sự thật nào"? –

+4

@KerrekSB "bất kỳ là đúng" = không "tất cả sai". Vì vậy, chỉ cần xác định 'all_false' theo các dòng tương tự và làm' template bằng cách sử dụng any_true = std :: integral_constant :: value>; ' –

+0

Ah, tất nhiên :-) –

2

Tôi nghĩ đơn giản sẽ được sử dụng std::initializer_list:

foo(std::initializer_list<double> args) 
{ 
    // Your stuff. 
} 

thay vì mẫu variadic. Có thể yêu cầu sử dụng {} thay vì/ngoài ()

+2

Điều này có thể gây phiền nhiễu để sử dụng, vì quy tắc chống lại việc thu hẹp chuyển đổi trong '{}'. –

+0

Tôi có một hàm tạo cho 'initializer_list >' rồi, và thêm một trường hợp khác cho trường hợp 'initializer_list ' sẽ là một lựa chọn không tốt cho giao diện của tôi vì một số hành vi khác nhau có ý nghĩa với nó. – user1997744

0

Đây là một cách tiếp cận chung – một TMP cho gấp nhị phân, sử dụng C++ 14. Trước tiên, hãy xác định các hoạt động kết hợp cơ bản:

#include <type_traits> 

struct and_op 
{ 
    using type = bool; 
    using identity = std::true_type; 
    template <bool A, bool B> static constexpr bool value = A && B; 
}; 

struct or_op 
{ 
    using type = bool; 
    using identity = std::false_type; 
    template <bool A, bool B> static constexpr bool value = A || B; 
}; 

Bây giờ thực tế fold cơ khí:

template <typename Op, typename Op::type...> 
struct fold; 

template <typename Op> 
struct fold<Op> : Op::identity {}; 

template <typename Op, typename Op::type Val> 
struct fold<Op, Val> 
    : std::integral_constant<typename Op::type 
    , Val> {}; 

template <typename Op, typename Op::type Val, typename Op::type... Tail> 
struct fold<Op, Val, Tail...> 
    : std::integral_constant<typename Op::type 
    , Op::template value<Val, fold<Op, Tail...>::value>> {}; 

Tiếp theo, chúng ta cần một cách để tạo ra những đặc điểm unary từ những đặc điểm nhị phân bằng cách liên kết:

template <template <typename, typename> class BPred, typename T> 
struct bind_pred 
{ 
    template <typename U> 
    struct pred_1st : std::integral_constant<bool, BPred<T, U>::value> {}; 
    template <typename U> 
    struct pred_2nd : std::integral_constant<bool, BPred<U, T>::value> {}; 
}; 

Cuối cùng, một trình bao bọc trợ giúp để kết hợp kết quả của việc áp dụng biến vị ngữ đơn nhất:

template <typename Op, template <typename> class UPred, typename ...Args> 
struct fold_pred : fold<Op, UPred<Args>::value...> {}; 

Vậy đó. Bây giờ chúng ta hãy làm việc:

template <typename T> 
using maybe_double = bind_pred<std::is_convertible, double>::pred_2nd<T>; 

#include <iomanip> 
#include <iostream> 

int main() 
{ 
    std::cout 
     << std::boolalpha 
     << fold_pred<and_op, maybe_double, int, float>::value << '\n' 
     << fold_pred<and_op, maybe_double, int, float, void>::value << '\n'; 
} 

Trong C++ 17 (hoặc C++ 1Z, chứ không phải), bạn có thể viết các giải pháp trực tiếp với mã nhờ ít hơn để các biểu thức lần mới. Ví dụ:

template <template <typename> class UPred, typename ...Args> 
static constexpr bool pred_all = (UPred<Args>::value && ...); 
//        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unary fold 

Cách sử dụng:

static_assert(pred_all<maybe_double, int, float>); 
+0

Ngay cả trong N4358, bạn vẫn có thể gấp '&&' trên một gói rỗng. –

+0

@ T.C .: Ồ, đúng vậy, '&&' và '||' thực sự được giữ lại. Tôi đang gỡ bỏ phần phụ huynh. –

1

Dưới đây là một () phiên bản (nặng nề lấy cảm hứng từ một trong những 's T.C. trên):

#include <type_traits> 

template <typename To, typename From, typename... R> 
struct are_all_convertible { 
    constexpr static bool value = std::is_convertible<From,To>::value && 
            are_all_convertible<To,R...>::value; 
}; 

template <typename To, typename From> 
struct are_all_convertible<To,From> { 
    constexpr static bool value = std::is_convertible<From,To>::value; 
}; 

template<typename... T, 
typename = typename std::enable_if<are_all_convertible<double, T...>::value>::type> 
foo(T... t){ /* code here */} 
Các vấn đề liên quan