E 'possibile creare tale caratteristica, con due limitazioni:
- per il compilatore, una funzione libera è qualcosa di fondamentalmente diverso da un funtore di classe che sovraccarica lo
operator()
. Quindi dobbiamo trattare entrambi i casi separatamente durante l'implementazione. Tuttavia, questo non è un problema per l'utilizzo, possiamo nascondere questi dettagli di implementazione all'utente.
- Abbiamo bisogno di conoscere la firma della funzione che si desidera chiamare. Questo di solito non è un problema, e ha il bell'effetto collaterale che il nostro tratto è in grado di gestire i sovraccarichi in modo abbastanza nativo.
Fase uno: Gratis funzioni
Cominciamo con le funzioni liberi, perché sono po 'più facile da individuare. Il nostro compito è, quando viene dato un puntatore a una funzione, determinare se la firma di quel puntatore a funzione corrisponde alla firma passata come secondo argomento modello. Per poterli confrontare, abbiamo bisogno di capire la firma della funzione sottostante o creare un puntatore a funzione della nostra firma. Ho arbitrariamente scelto la seconda:
// build R (*)(Args...) from R (Args...)
// compile error if signature is not a valid function signature
template <typename, typename>
struct build_free_function;
template <typename F, typename R, typename ... Args>
struct build_free_function<F, R (Args...)>
{ using type = R (*)(Args...); };
Ora tutto quello che resta da fare è quello di confrontare e abbiamo finito con la funzione di parte libera:
// determine whether a free function pointer F has signature S
template <typename F, typename S>
struct is_function_with_signature
{
// check whether F and the function pointer of S are of the same
// type
static bool constexpr value = std::is_same<
F, typename build_free_function<F, S>::type
>::value;
};
Fase due: funtori Classe
Questo è un po 'più coinvolto. Si potrebbe facilmente rilevare con SFINAE se una classe definisce un operator()
:
template <typename T>
struct defines_functor_operator
{
typedef char (& yes)[1];
typedef char (& no)[2];
// we need a template here to enable SFINAE
template <typename U>
static yes deduce(char (*)[sizeof(&U::operator())]);
// fallback
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes);
};
ma questo non ci dice se esiste uno per la nostra firma funzione desiderata! Fortunatamente, possiamo usare un trucco qui: i puntatori sono parametri di template validi. Così siamo in grado di utilizzare prima il puntatore a funzione membro della nostra firma desiderata, e controllare se &T::operator()
è di quel tipo:
template <typename T, T> struct check;
Ora check<void (C::*)() const, &C::operator()>
sarà solo un modello di istanza valida se C
ha effettivamente un void C::operator()() const
. Ma per fare questo dobbiamo prima combinare C
e la firma in un puntatore funzione membro. Come abbiamo già visto, dobbiamo preoccuparci di due casi extra di cui non abbiamo avuto a cuore le funzioni gratuite: const
e volatile
.Oltre a questo è più o meno lo stesso:
// build R (C::*)(Args...) from R (Args...)
// R (C::*)(Args...) const from R (Args...) const
// R (C::*)(Args...) volatile from R (Args...) volatile
// compile error if signature is not a valid member function signature
template <typename, typename>
struct build_class_function;
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...)>
{ using type = R (C::*)(Args...); };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) const>
{ using type = R (C::*)(Args...) const; };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) volatile>
{ using type = R (C::*)(Args...) volatile; };
Mettendo questo e le nostre scoperte, relativa alla struct check
aiutante insieme, otteniamo il nostro metafunction di controllo per gli oggetti funtore:
// determine whether a class C has an operator() with signature S
template <typename C, typename S>
struct is_functor_with_signature
{
typedef char (& yes)[1];
typedef char (& no)[2];
// helper struct to determine that C::operator() does indeed have
// the desired signature; &C::operator() is only of type
// R (C::*)(Args...) if this is true
template <typename T, T> struct check;
// T is needed to enable SFINAE
template <typename T> static yes deduce(check<
typename build_class_function<C, S>::type, &T::operator()> *);
// fallback if check helper could not be built
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
};
Fase tre: Mettere i pezzi insieme
Abbiamo quasi finito. Ora abbiamo solo bisogno di decidere quando utilizzare la nostra funzione gratuita e quando i metafunzionamenti del functor della classe. Fortunatamente, C++ 11 ci fornisce un tratto std::is_class
che possiamo usare per questo. Quindi tutto quello che dobbiamo fare è specializzarsi su un parametro booleano:
// C is a class, delegate to is_functor_with_signature
template <typename C, typename S, bool>
struct is_callable_impl
: std::integral_constant<
bool, is_functor_with_signature<C, S>::value
>
{};
// F is not a class, delegate to is_function_with_signature
template <typename F, typename S>
struct is_callable_impl<F, S, false>
: std::integral_constant<
bool, is_function_with_signature<F, S>::value
>
{};
in modo che possiamo finalmente aggiungere l'ultimo pezzo del puzzle, essendo il nostro attuale is_callable
tratto:
// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
: is_callable_impl<
Callable, Signature,
std::is_class<Callable>::value
>
{};
Ora ripulire il nostro codice, inserire i dettagli dell'implementazione in namespace anonimi in modo che non siano accessibili al di fuori del nostro file e avere un buon is_callable.hpp
da utilizzare nel nostro progetto.
codice Figura
namespace // implementation detail
{
// build R (*)(Args...) from R (Args...)
// compile error if signature is not a valid function signature
template <typename, typename>
struct build_free_function;
template <typename F, typename R, typename ... Args>
struct build_free_function<F, R (Args...)>
{ using type = R (*)(Args...); };
// build R (C::*)(Args...) from R (Args...)
// R (C::*)(Args...) const from R (Args...) const
// R (C::*)(Args...) volatile from R (Args...) volatile
// compile error if signature is not a valid member function signature
template <typename, typename>
struct build_class_function;
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...)>
{ using type = R (C::*)(Args...); };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) const>
{ using type = R (C::*)(Args...) const; };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) volatile>
{ using type = R (C::*)(Args...) volatile; };
// determine whether a class C has an operator() with signature S
template <typename C, typename S>
struct is_functor_with_signature
{
typedef char (& yes)[1];
typedef char (& no)[2];
// helper struct to determine that C::operator() does indeed have
// the desired signature; &C::operator() is only of type
// R (C::*)(Args...) if this is true
template <typename T, T> struct check;
// T is needed to enable SFINAE
template <typename T> static yes deduce(check<
typename build_class_function<C, S>::type, &T::operator()> *);
// fallback if check helper could not be built
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
};
// determine whether a free function pointer F has signature S
template <typename F, typename S>
struct is_function_with_signature
{
// check whether F and the function pointer of S are of the same
// type
static bool constexpr value = std::is_same<
F, typename build_free_function<F, S>::type
>::value;
};
// C is a class, delegate to is_functor_with_signature
template <typename C, typename S, bool>
struct is_callable_impl
: std::integral_constant<
bool, is_functor_with_signature<C, S>::value
>
{};
// F is not a class, delegate to is_function_with_signature
template <typename F, typename S>
struct is_callable_impl<F, S, false>
: std::integral_constant<
bool, is_function_with_signature<F, S>::value
>
{};
}
// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
: is_callable_impl<
Callable, Signature,
std::is_class<Callable>::value
>
{};
Ideone esempio con alcuni test
http://ideone.com/7PWdiv
come dire 'is_function :: value'? –
Fiktik
http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 potrebbe interessarti. – pmr
Vuoi testare solo i funtori o qualsiasi oggetto callable? Sembra che un uso di SFINAE del carattere 'result_of' possa funzionare per identificare qualsiasi tipo di chiamabile. Sono un po 'sorpreso dal fatto che non ci sia già alcun tratto di 'std :: is_callable'. – bames53