2015-04-16 12 views
11

Poiché esiste una restrizione su allowed non-type variadic templates, sto tentando di scrivere una funzione prendendo un numero arbitrario di doppi utilizzando enable_if. In sostanza, voglio fare qualcosa di simile:Controllo del tipo di pacco parametri utilizzando enable_if

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

che sto scegliendo di mettere il enable_if come un valore predefinito per un parametro senza nome dal momento che la mia funzione è in realtà un costruttore e non avrà un valore di ritorno. Ciò funzionerebbe per un singolo parametro, ma poiché si tratta di un modello variadiale, T è un pacchetto di parametri e il codice sopra riportato non è valido. Quindi, come posso verificare che ogni parametro sia convertibile in un doppio?

risposta

17

Il bool_pack trucco di nuovo.

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

Poi

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

infine

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

Neat, ma non ha un'ovvia generalizzazione a "any is true"? –

+4

@KerrekSB "any is true" = non "tutto falso". Quindi definisci 'all_false' lungo linee simili e fai' template usando any_true = std :: integral_constant :: value>; ' –

+0

Ah, ovviamente :-) –

2

penso che il più semplice sarebbe quella di utilizzare std::initializer_list:

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

invece di template variadic. Si può richiedere di utilizzare {} invece di/in aggiunta a ()

+2

Questo può essere fastidioso da usare, a causa della regola contro il restringimento delle conversioni in '{}'. –

+0

Ho già un costruttore per 'initializer_list >' e aggiungerne un altro per il caso 'initializer_list ' sarebbe una cattiva scelta per la mia interfaccia perché molti comportamenti diversi hanno senso per questo. – user1997744

0

Qui è un approccio generico – una TMP per la piegatura binario, usando C++ 14. In primo luogo, definiamo le operazioni che conciliano la base:

#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; 
}; 

Ora l'attuale fold meccanico:

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>> {}; 

Avanti, abbiamo bisogno di un modo per creare tratti unarie da tratti binari legandosi:

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> {}; 
}; 

Infine, un wrapper di supporto per combinare il risultato dell'applicazione di un predicato unario:

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

Questo è tutto. Ora veniamo al lavoro:

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'; 
} 

In C++ 17 (o C++ 1z, piuttosto), è possibile scrivere soluzioni dirette con codice meno grazie alle nuove espressioni piega. Per esempio:

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

Usage:

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

Anche in N4358, puoi ancora foldare '&&' su un pacchetto vuoto. –

+0

@ T.C .: Oh, giusto, '&&' e '||' sono in realtà tenuti in giro. Sto rimuovendo il parentetico. –

1

Ecco un altro () versione (pesantemente ispirato da uno s' il T.C. sopra):

#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 */} 
Problemi correlati