Domanda

Sto chiedendo per un modello di trucco per rilevare se una classe ha una specifica funzione membro di una determinata firma.

Il problema è simile a quello citato qui http://www.gotw.ca/gotw/071.htm ma non è la stessa:nella voce di Sutter libro ha risposto alla domanda che una classe C DEVE FORNIRE una funzione membro con una particolare firma, altrimenti il programma non compila.Nel mio problema ho bisogno di fare qualcosa se una classe ha quella funzione, se non a "qualcosa d'altro".

Un problema simile è stato affrontato da boost::serializzazione, ma non mi piace la soluzione adottata:una funzione di modello che richiama per impostazione predefinita, la funzione (che è necessario definire) con una particolare firma, a meno che si definisce una particolare funzione membro (nel loro caso "serializzare" che accetta 2 parametri di un determinato tipo) con una particolare firma, altrimenti un errore di compilazione succede.Che è quello di implementare sia invadente e non intrusivo per la serializzazione.

Non mi piace la soluzione per due motivi:

  1. Non è invadente, è necessario sovrascrivere globale "serializzare" funzione di boost::serializzazione dello spazio dei nomi, in modo che avete IN VOSTRO CODICE CLIENTE per aprire spazio dei nomi boost e lo spazio dei nomi di serializzazione!
  2. Lo stack per risolvere pasticcio di 10 a 12 chiamate di funzione.

Ho bisogno di definire un comportamento personalizzato per le classi il quale non ha funzione di membro, e i miei soggetti sono all'interno di diversi spazi dei nomi (e non voglio ignorare una funzione globale definita in uno spazio dei nomi, mentre io sono in un altro)

Potete darmi un suggerimento per risolvere questo enigma?

È stato utile?

Soluzione

Non so se ho capito bene, ma si può sfruttare SFINAE per la funzione di rilevamento della presenza a tempo di compilazione.Esempio mio codice (test se la classe ha funzione membro size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
    template<typename U, size_t (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
    template<typename U> static int Test(...);
    static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
        // We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
    ReportMemUsage(m, 
        std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}

Altri suggerimenti

Ecco una possibile implementazione di affidarsi C++11 caratteristiche.Si rileva correttamente la funzione, anche se è ereditaria (a differenza della soluzione accettate risposta, come Mike Kinghan osserva in la sua risposta).

La funzione di questo frammento di test è chiamato serialize:

#include <type_traits>

// Primary template with a static assertion
// for a meaningful error message
// if it ever gets instantiated.
// We could leave it undefined if we didn't care.

template<typename, typename T>
struct has_serialize {
    static_assert(
        std::integral_constant<T, false>::value,
        "Second template parameter needs to be of function type.");
};

// specialization that does the checking

template<typename C, typename Ret, typename... Args>
struct has_serialize<C, Ret(Args...)> {
private:
    template<typename T>
    static constexpr auto check(T*)
    -> typename
        std::is_same<
            decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
            Ret    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        >::type;  // attempt to call it and see if the return type is correct

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

Utilizzo:

struct X {
     int serialize(const std::string&) { return 42; } 
};

struct Y : X {};

std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1

Accettato di rispondere a questa domanda di fase di compilazione socio-funzione introspezione, anche se è giustamente popolare, ha un intoppo che può essere osservato nel seguente programma:

#include <type_traits>
#include <iostream>
#include <memory>

/*  Here we apply the accepted answer's technique to probe for the
    the existence of `E T::operator*() const`
*/
template<typename T, typename E>
struct has_const_reference_op
{
    template<typename U, E (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::operator*>*);
    template<typename U> static int Test(...);
    static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};

using namespace std;

/* Here we test the `std::` smart pointer templates, including the
    deprecated `auto_ptr<T>`, to determine in each case whether
    T = (the template instantiated for `int`) provides 
    `int & T::operator*() const` - which all of them in fact do.
*/ 
int main(void)
{
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl;
    return 0;
}

Costruito con GCC 4.6.3, il programma di uscite 110 - ci informa che T = std::shared_ptr<int> non non fornire int & T::operator*() const.

Se non siete già consigliato questo gotcha, poi uno sguardo al di la definizione di std::shared_ptr<T> nell'intestazione <memory> per far luce.In che attuazione, std::shared_ptr<T> è derivata da una classe base da cui eredita operator*() const.Così il modello di creazione di un'istanza SFINAE<U, &U::operator*> che costituisce "la ricerca di" operatore per U = std::shared_ptr<T> non accadrà, perché std::shared_ptr<T> non ha operator*() la sua istanza modello non "fare eredità".

Questo intoppo non pregiudica il noto SFINAE approccio, tramite "Il sizeof() Trucco", per la rilevazione solo se T ha qualche funzione membro mf (vedi ad es.questa risposta e i commenti).Ma di stabilire che T::mf esiste spesso (di solito?) non è abbastanza buono:si può anche la necessità di stabilire, che ha una firma desiderata.Che è dove il illustrata la tecnica di punteggi.Il pointerized variante della firma è inscritto in un parametro di un tipo di modello che deve essere soddisfatta da &T::mf per il SFINAE sonda per avere successo.Ma questo modello di creazione di un'istanza tecnica dà la risposta sbagliata, quando T::mf è ereditato.

Cassetta di sicurezza SFINAE tecnica per la fase di compilazione di introspezione T::mf deve evitare il l'uso di &T::mf all'interno di un argomento di template per creare un'istanza di un tipo su cui SFINAE modello di funzione di risoluzione dipende.Invece, SFINAE funzione di modello la risoluzione non può dipendere solo su esattamente pertinenti dichiarazioni del tipo utilizzato come argomento i tipi di overload SFINAE funzione di tastatura.

Per una risposta alla domanda che rimane da questo vincolo io illustrare per rilevamento di fase di compilazione E T::operator*() const, per arbitrario T e E.Lo stesso schema si applica mutatis mutandis, sonda per qualsiasi altro membro firma del metodo.

#include <type_traits>

/*! The template `has_const_reference_op<T,E>` exports a
    boolean constant `value that is true iff `T` provides
    `E T::operator*() const`
*/ 
template< typename T, typename E>
struct has_const_reference_op
{
    /* SFINAE operator-has-correct-sig :) */
    template<typename A>
    static std::true_type test(E (A::*)() const) {
        return std::true_type();
    }

    /* SFINAE operator-exists :) */
    template <typename A> 
    static decltype(test(&A::operator*)) 
    test(decltype(&A::operator*),void *) {
        /* Operator exists. What about sig? */
        typedef decltype(test(&A::operator*)) return_type; 
        return return_type();
    }

    /* SFINAE game over :( */
    template<typename A>
    static std::false_type test(...) {
        return std::false_type(); 
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<T>(0,0)) type;

    static const bool value = type::value; /* Which is it? */
};

In questa soluzione, il sovraccarico SFINAE funzione di tastatura: test() è "richiamato in modo ricorsivo".(Naturalmente non è effettivamente attivata;semplicemente ha il ritorno tipi di ipotetici invocazioni risolto dal compilatore.)

Abbiamo bisogno di sonda per almeno uno e al massimo due punti di informazione:

  • Non T::operator*() esiste affatto?Se non, abbiamo finito.
  • Dato che T::operator*() esiste, è la sua firma E T::operator*() const?

Otteniamo le risposte valutando il tipo di ritorno di una singola chiamata per test(0,0).Fatto da:

    typedef decltype(test<T>(0,0)) type;

Questa chiamata potrebbe essere risolto /* SFINAE operator-exists :) */ sovraccarico di test(), o potrebbe risolvere il /* SFINAE game over :( */ sovraccarico.Non è possibile risolvere il /* SFINAE operator-has-correct-sig :) */ sovraccarico, perché uno si aspetta un solo argomento e passiamo due.

Perché stiamo passando due?Semplicemente per forzare la risoluzione di escludere /* SFINAE operator-has-correct-sig :) */.Il secondo argomento non ha altri signifance.

Questa chiamata a test(0,0) si risolve in /* SFINAE operator-exists :) */ solo nel caso in cui il primo parametro 0 risponde con efficienza il primo parametro tipo di sovraccarico, che è decltype(&A::operator*), con A = T.0 in grado di soddisfare quel tipo solo nel caso in cui T::operator* esiste.

Supponiamo che il compilatore dice Sì a che.Poi va con /* SFINAE operator-exists :) */ e deve determinare il tipo di ritorno di la chiamata di funzione, che in questo caso è decltype(test(&A::operator*)) - il tipo di ritorno di un'altra chiamata test().

Questa volta, stiamo passando un solo argomento, &A::operator*, che abbiamo ora si conosce l'esistenza, o non saremmo qui.Una chiamata a test(&A::operator*) potrebbe risolvere per /* SFINAE operator-has-correct-sig :) */ o, ancora, per potrebbe risolvere /* SFINAE game over :( */.La chiamata verrà partita /* SFINAE operator-has-correct-sig :) */ solo nel caso in cui &A::operator* soddisfa l'unico parametro tipo di sovraccarico, che è E (A::*)() const, con A = T.

Il compilatore potrà dire di Sì, qui se T::operator* è quella della firma, e poi, ancora una volta, per valutare il tipo di ritorno del sovraccarico.Non più "ricorsioni" ora:è std::true_type.

Se il compilatore non scegliere /* SFINAE operator-exists :) */ per il chiamata test(0,0) o non scegliere /* SFINAE operator-has-correct-sig :) */ per la chiamata test(&A::operator*), quindi in entrambi i casi si va con /* SFINAE game over :( */ e la finale di ritorno è di tipo std::false_type.

Qui è un programma di test che mostra il modello di produzione previsto risposte nel campione variegato di casi (GCC 4.6.3 di nuovo).

// To test
struct empty{};

// To test 
struct int_ref
{
    int & operator*() const {
        return *_pint;
    }
    int & foo() const {
        return *_pint;
    }
    int * _pint;
};

// To test 
struct sub_int_ref : int_ref{};

// To test 
template<typename E>
struct ee_ref
{
    E & operator*() {
        return *_pe;
    }
    E & foo() const {
        return *_pe;
    }
    E * _pe;
};

// To test 
struct sub_ee_ref : ee_ref<char>{};

using namespace std;

#include <iostream>
#include <memory>
#include <vector>

int main(void)
{
    cout << "Expect Yes" << endl;
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value;
    cout << has_const_reference_op<std::vector<int>::iterator,int &>::value;
    cout << has_const_reference_op<std::vector<int>::const_iterator,
            int const &>::value;
    cout << has_const_reference_op<int_ref,int &>::value;
    cout << has_const_reference_op<sub_int_ref,int &>::value  << endl;
    cout << "Expect No" << endl;
    cout << has_const_reference_op<int *,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,char &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int const &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int>::value;
    cout << has_const_reference_op<unique_ptr<long>,int &>::value;
    cout << has_const_reference_op<int,int>::value;
    cout << has_const_reference_op<std::vector<int>,int &>::value;
    cout << has_const_reference_op<ee_ref<int>,int &>::value;
    cout << has_const_reference_op<sub_ee_ref,int &>::value;
    cout << has_const_reference_op<empty,int &>::value  << endl;
    return 0;
}

Ci sono nuovi difetti in questa idea?Può essere reso più generico senza, ancora una volta, cadere fallo di intoppo evita?

Qui sono alcuni di utilizzo snippet:*Il coraggio per tutto questo, sono più in basso

Controllare per gli stati x in una determinata classe.Potrebbe essere var, func, di classe, di unione, o enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

Di controllo per la funzione di membro void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

Verificare la variabile membro x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Controllare membro della classe x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Controllare per gli stati dell'unione x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Controllare per gli stati enum x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Controllare per ogni funzione membro x indipendentemente dalla firma:

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

O

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

Dettagli e core:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

Macro (El Diablo!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

Questo dovrebbe essere sufficiente, se si conosce il nome della funzione membro che ti aspetti.(In questo caso, la funzione bla non riesce a creare un'istanza se non c'è la funzione membro (la scrittura di uno che lavora comunque è molto difficile, perché c'è una mancanza di funzione parziale di specializzazione.Potrebbe essere necessario utilizzare modelli di classe), Inoltre, l'abilitazione struct (che è simile a enable_if) potrebbe anche essere basato su modelli il tipo di funzione che si desidera avere come membro.

template <typename T, int (T::*) ()> struct enable { typedef T type; };
template <typename T> typename enable<T, &T::i>::type bla (T&);
struct A { void i(); };
struct B { int i(); };
int main()
{
  A a;
  B b;
  bla(b);
  bla(a);
}

Qui è più semplice prendere su di Mike Kinghan risposta.Questo consentirà di rilevare i metodi ereditati.Verifica anche per il esatto firma (a differenza di jrok approccio che permette argomento conversioni).

template <class C>
class HasGreetMethod
{
    template <class T>
    static std::true_type testSignature(void (T::*)(const char*) const);

    template <class T>
    static decltype(testSignature(&T::greet)) test(std::nullptr_t);

    template <class T>
    static std::false_type test(...);

public:
    using type = decltype(test<C>(nullptr));
    static const bool value = type::value;
};

struct A { void greet(const char* name) const; };
struct Derived : A { };
static_assert(HasGreetMethod<Derived>::value, "");

Eseguibile esempio

È possibile utilizzare std::is_member_function_pointer

class A {
   public:
     void foo() {};
}

 bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;

È venuto con lo stesso tipo di problema a me stesso, e trovare le soluzioni proposte qui, molto interessante...ma aveva l'esigenza di una soluzione che:

  1. Rileva ereditato le funzioni;
  2. È compatibile con i non C++11 pronto compilatori (quindi non decltype)

Trovato un altro thread proporre qualcosa di simile, basato su un BOOST discussione.Qui è la generalizzazione della soluzione proposta come due macro dichiarazione per i tratti di classe, secondo il modello di boost::has_* classi.

#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/vector.hpp>

/// Has constant function
/** \param func_ret_type Function return type
    \param func_name Function name
    \param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \
    __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__)

/// Has non-const function
/** \param func_ret_type Function return type
    \param func_name Function name
    \param ... Variadic arguments are for the function parameters
*/
#define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \
    __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__)

// Traits content
#define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...)  \
    template                                                                  \
    <   typename Type,                                                        \
        bool is_class = boost::is_class<Type>::value                          \
    >                                                                         \
    class has_func_ ## func_name;                                             \
    template<typename Type>                                                   \
    class has_func_ ## func_name<Type,false>                                  \
    {public:                                                                  \
        BOOST_STATIC_CONSTANT( bool, value = false );                         \
        typedef boost::false_type type;                                       \
    };                                                                        \
    template<typename Type>                                                   \
    class has_func_ ## func_name<Type,true>                                   \
    {   struct yes { char _foo; };                                            \
        struct no { yes _foo[2]; };                                           \
        struct Fallback                                                       \
        {   func_ret_type func_name( __VA_ARGS__ )                            \
                UTILITY_OPTIONAL(func_const,const) {}                         \
        };                                                                    \
        struct Derived : public Type, public Fallback {};                     \
        template <typename T, T t>  class Helper{};                           \
        template <typename U>                                                 \
        static no deduce(U*, Helper                                           \
            <   func_ret_type (Fallback::*)( __VA_ARGS__ )                    \
                    UTILITY_OPTIONAL(func_const,const),                       \
                &U::func_name                                                 \
            >* = 0                                                            \
        );                                                                    \
        static yes deduce(...);                                               \
    public:                                                                   \
        BOOST_STATIC_CONSTANT(                                                \
            bool,                                                             \
            value = sizeof(yes)                                               \
                == sizeof( deduce( static_cast<Derived*>(0) ) )               \
        );                                                                    \
        typedef ::boost::integral_constant<bool,value> type;                  \
        BOOST_STATIC_CONSTANT(bool, is_const = func_const);                   \
        typedef func_ret_type return_type;                                    \
        typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;                \
    }

// Utility functions
#define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ )
#define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ )
#define __UTILITY_OPTIONAL_0(...)
#define __UTILITY_OPTIONAL_1(...) __VA_ARGS__

Queste macro espandere a una classe di tratti con il seguente prototipo:

template<class T>
class has_func_[func_name]
{
public:
    /// Function definition result value
    /** Tells if the tested function is defined for type T or not.
    */
    static const bool value = true | false;

    /// Function definition result type
    /** Type representing the value attribute usable in
        http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html
    */
    typedef boost::integral_constant<bool,value> type;

    /// Tested function constness indicator
    /** Indicates if the tested function is const or not.
        This value is not deduced, it is forced depending
        on the user call to one of the traits generators.
    */
    static const bool is_const = true | false;

    /// Tested function return type
    /** Indicates the return type of the tested function.
        This value is not deduced, it is forced depending
        on the user's arguments to the traits generators.
    */
    typedef func_ret_type return_type;

    /// Tested function arguments types
    /** Indicates the arguments types of the tested function.
        This value is not deduced, it is forced depending
        on the user's arguments to the traits generators.
    */
    typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;
};

Così che cosa è l'uso tipico che si può fare al di fuori di questo?

// We enclose the traits class into
// a namespace to avoid collisions
namespace ns_0 {
    // Next line will declare the traits class
    // to detect the member function void foo(int,int) const
    DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int);
}

// we can use BOOST to help in using the traits
#include <boost/utility/enable_if.hpp>

// Here is a function that is active for types
// declaring the good member function
template<typename T> inline
typename boost::enable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{   _this_.foo(a,b);
}

// Here is a function that is active for types
// NOT declaring the good member function
template<typename T> inline
typename boost::disable_if< ns_0::has_func_foo<T> >::type
foo_bar(const T &_this_, int a=0, int b=1)
{   default_foo(_this_,a,b);
}

// Let us declare test types
struct empty
{
};
struct direct_foo
{
    void foo(int,int);
};
struct direct_const_foo
{
    void foo(int,int) const;
};
struct inherited_const_foo :
    public direct_const_foo
{
};

// Now anywhere in your code you can seamlessly use
// the foo_bar function on any object:
void test()
{
    int a;
    foo_bar(a); // calls default_foo

    empty b;
    foo_bar(b); // calls default_foo

    direct_foo c;
    foo_bar(c); // calls default_foo (member function is not const)

    direct_const_foo d;
    foo_bar(d); // calls d.foo (member function is const)

    inherited_const_foo e;
    foo_bar(e); // calls e.foo (inherited member function)
}

Per realizzare questo abbiamo la necessità di utilizzare:

  1. Template di funzione di sovraccarico con diversi tipi di ritorno, a seconda che il metodo è disponibile
  2. In linea con il meta-istruzioni condizionali nella type_traits intestazione, ti desidera restituire un true_type o false_type dal nostro sovraccarichi
  3. Dichiarare la true_type sovraccarico, in attesa di una int e il false_type sovraccarico in attesa Variadic Parametri di sfruttare: "La priorità sul pulsante con i puntini di conversione nella risoluzione dell'overload"
  4. Nel definire il modello di disciplinare per la true_type funzione useremo declval e decltype che ci permette di rilevare la funzione indipendente di ritorno tipo di differenze o sovraccarichi tra metodi

Si può vedere un esempio vivo di questo qui. Ma io anche spiegare di seguito:

Voglio verificare l'esistenza di una funzione denominata test che prende un tipo convertibile da int, quindi avrei bisogno di dichiarare queste due funzioni:

template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int);
template <typename T> static false_type hasTest(...);
  • decltype(hasTest<a>(0))::value è true (Nota: non c'è bisogno di creare una funzionalità speciale per affrontare la void a::test() il sovraccarico, il void a::test(int) è accettato)
  • decltype(hasTest<b>(0))::value è true (Perché int è trasformabile a double int b::test(double) è accettato, indipendente dal tipo di ritorno)
  • decltype(hasTest<c>(0))::value è false (c non avere un metodo chiamato test che accetta un tipo convertibile da int quindi questa non è accettato)

Questa soluzione ha 2 inconvenienti:

  1. Richiede un metodo per la dichiarazione di una coppia di funzioni
  2. Crea spazio dei nomi di inquinamento particolarmente se si desidera eseguire il test per nomi simili, per esempio, cosa ci sarebbe il nome di una funzione che ha voluto mettere alla prova per un test() metodo?

Quindi è importante che queste funzioni siano dichiarati in dettagli dello spazio dei nomi, o meglio se sono solo per essere utilizzato con una classe, dovrebbero essere dichiarati privata da quella classe.A tal fine ho scritto una macro per aiutare abstract queste informazioni:

#define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \
                              template <typename T> static false_type __ ## DEFINE(...); \
                              template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0));

È possibile utilizzare questo come:

namespace details {
    FOO(test(declval<int>()), test_int)
    FOO(test(), test_void)
}

Successivamente chiamata details::test_int<a>::value o details::test_void<a>::value sarebbe resa true o false ai fini del codice inline o meta-programmazione.

Per non essere invadente, si può anche mettere serialize nello spazio dei nomi della classe pubblicati, o l'archivio della categoria, grazie a Ricerca Koenig.Vedere Gli spazi dei nomi per una Funzione Override per ulteriori dettagli.:-)

L'apertura di un dato spazio dei nomi per implementare una funzione è Semplicemente Sbagliato.(ad esempio, non si dovrebbe aprire spazio dei nomi std per implementare swap per i propri tipi, ma dovrebbe utilizzare ricerca Koenig, invece.)

Va bene.Seconda prova.Va bene se non ti piace questo sia, sto cercando di più le idee.

Herb Sutter articolo parla di tratti.Così si può avere un tratti di classe predefinita in cui istanza è il ripiego di comportamento, e per ogni classe dove gli funzione esiste, quindi i tratti di classe è specializzata per richiamare la funzione membro.Credo Erbe l'articolo cita una tecnica per fare questo in modo che non comportano un sacco di copia e incolla.

Come ho detto, anche se, forse non si desidera l'aggravio di lavoro con "marcatura" le classi che implementano tale membro.In questo caso, sto guardando una terza soluzione....

Senza C++11 di supporto (decltype) questo potrebbe funzionare:

SSCCE

#include <iostream>
using namespace std;

struct A { void foo(void); };
struct Aa: public A { };
struct B { };

struct retA { int foo(void); };
struct argA { void foo(double); };
struct constA { void foo(void) const; };
struct varA { int foo; };

template<typename T>
struct FooFinder {
    typedef char true_type[1];
    typedef char false_type[2];

    template<int>
    struct TypeSink;

    template<class U>
    static true_type &match(U);

    template<class U>
    static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);

    template<class U>
    static false_type &test(...);

    enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
};

int main() {
    cout << FooFinder<A>::value << endl;
    cout << FooFinder<Aa>::value << endl;
    cout << FooFinder<B>::value << endl;

    cout << FooFinder<retA>::value << endl;
    cout << FooFinder<argA>::value << endl;
    cout << FooFinder<constA>::value << endl;
    cout << FooFinder<varA>::value << endl;
}

Come si spera di opere

A, Aa e B sono clases in questione, Aa essere speciale quello che eredita il membro che stiamo cercando.

Nel FooFinder il true_type e false_type sono le sostituzioni per il corrispondente C++11 classi.Anche per la comprensione del modello di meta-programmazione, rivelano la base di SFINAE-sizeof-trucco.

Il TypeSink è un modello di struttura che viene utilizzato in seguito per lavabo integrale risultato del sizeof operatore in un modello di istanza per formare un tipo.

Il match la funzione è un'altra SFINAE tipo di modello che è rimasto senza un equivalente generico.Si può quindi solo essere istanziato se il tipo di argomento corrisponde il tipo che si era specializzata per.

Sia il test le funzioni insieme con la dichiarazione di enumerazione, infine, il modulo centrale SFINAE modello.C'è un generico uso dei puntini di sospensione che restituisce il false_type e una controparte con più argomenti specifici di prendere il sopravvento.

Per essere in grado di creare un'istanza del test funzione con un argomento del modello di T, il match la funzione deve essere creata un'istanza, come tipo di ritorno è necessario creare un'istanza del TypeSink argomento.L'avvertenza è che &U::foo, di essere avvolto in un argomento di una funzione, è non di cui a all'interno di un argomento del modello di specializzazione, in modo da membro ereditato ricerca è ancora in atto.

Io credo che la risposta che cerchi è qui.

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

e un po ' più riempito esempio qui

http://pastie.org/298994

Io uso la tecnica per rilevare la presenza di un supporto ostream operatore << la classe in questione e quindi generare un diverso pezzo di codice a seconda.

Non credevo fosse possibile prima di trovare il collegato soluzione, ma è un trucchetto.Trascorrere il tempo di capire il codice, ed è molto vale la pena.

Brad

Se si utilizza facebook follia, loro sono al di fuori della casella di macro per aiutarvi a:

#include <folly/Traits.h>
namespace {
  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
} // unnamed-namespace

void some_func() {
  cout << "Does class Foo have a member int test() const? "
    << boolalpha << has_test_traits<Foo, int() const>::value;
}

Anche se i dettagli di implementazione è la stessa con la risposta precedente, utilizzare una libreria, è più semplice.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top