In primo luogo, un po 'boilerplate per darci una prova SFINAE tipo amichevole invoke:
namespace invoke_details {
template<class Sig,class=void> struct invoke {};
template<class F, class...Args> struct invoke<
F(Args...),
void(decltype(std::declval<F>(Args...)))
> {
using type=decltype(std::declval<F>(Args...));
};
}
template<class Sig> using invoke=typename invoke_details::invoke<Sig>::type;
ora invoke< Foo(int, int) >
è il tipo che si ottiene quando si prende una variabile di tipo Foo
e invocare con due int
s, ed è valuta in modo amichevole con SFINAE.
Questo è fondamentalmente un amichevole SFINAE std::result_of
.
Successivamente, alcune cose più belle. result_type
e param_type
risparmiare sulle digitando altrove:
template<class T>
using result_type = typename T::result_type;
template<class T>
using param_type = typename T::param_type;
details::has_property< X, T >
avrà un modello X
e applicare T
.Se questo succede, è true_type
, altrimenti false_type
:
namespace details {
template<template<class>class X, class T, class=void>
struct has_property : std::false_type {};
template<template<class>class X, class T>
struct has_property<X,T,void(X<T>)> : std::true_type {};
}
Questo ci dà has_result_type
ecc in un modo piuttosto:
template<class T>
using has_result_type = details::has_property< result_type, T >;
template<class T>
using has_param_type = details::has_property< param_type, T >;
template<class Sig>
using can_invoke = details::has_property< invoke, Sig >;
template<class T>
using can_twist_invoke = can_invoke< T(std::mt19937) >;
Credo che la semplicità di queste dichiarazioni vale la boilerplate in precedenza.
Ora, un po 'di metaprogrammazione booleano:
template<bool...> struct all_of : std::true_type {};
template<bool b0, bool... bs> struct all_of : std::integral_constant< bool, b0 && all_of<bs...>{} > {};
template<class T, template<class>class... Tests>
using passes_tests = all_of< Tests<T>{}... >;
e otteniamo il nostro una linea abbastanza is_distribution
:
template<class T>
using is_distribution = passes_tests< T, has_result_type, has_param_type, can_twist_invoke >;
Questo non copre ancora .param
o .reset
.
Questo stile porta a più codice, ma la "cattiva" roba viene nascosta nei dettagli degli spazi dei nomi. Qualcuno che vede is_distribution
può guardare la definizione e vedere cosa si intende per autodocumentazione. Solo dopo aver eseguito il drill down vedranno i dettagli di implementazione più confusi.
Se si accettano tutte le distribuzioni, è necessario assicurarsi che il tipo sia valido per la distribuzione, altrimenti è un comportamento non definito. –
@remyabel Che tipo? Pensavo che tutte le distribuzioni possano utilizzare un generatore standard come "mt19937". –
Qual è la tua definizione di "solo distribuzioni". Nella mia mente tutto ciò che soddisfa l'interfaccia implicita di una distribuzione è una distribuzione. –