Überprüfen Sie, ob eine Klasse eine Member-Funktion einer bestimmten Signatur hat

Lesezeit: 20 Minuten

Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
ugasoft

Ich bitte um einen Vorlagentrick, um zu erkennen, ob eine Klasse eine bestimmte Member-Funktion einer bestimmten Signatur hat.

Das Problem ist ähnlich wie das hier genannte
http://www.gotw.ca/gotw/071.htm
aber nicht dasselbe: In dem Artikel von Sutters Buch antwortete er auf die Frage, dass eine Klasse C eine Elementfunktion mit einer bestimmten Signatur BEREITSTELLEN MUSS, sonst wird das Programm nicht kompiliert. In meinem Problem muss ich etwas tun, wenn eine Klasse diese Funktion hat, sonst “etwas anderes”.

Ein ähnliches Problem hatte boost::serialization, aber ich mag die Lösung nicht, die sie angenommen haben: eine Template-Funktion, die standardmäßig eine freie Funktion (die Sie definieren müssen) mit einer bestimmten Signatur aufruft, es sei denn, Sie definieren eine bestimmte Member-Funktion ( in ihrem Fall “serialisieren”, das 2 Parameter eines bestimmten Typs benötigt) mit einer bestimmten Signatur, sonst tritt ein Kompilierungsfehler auf. Das heißt, sowohl aufdringliche als auch nicht-aufdringliche Serialisierung zu implementieren.

Ich mag diese Lösung aus zwei Gründen nicht:

  1. Um nicht aufdringlich zu sein, müssen Sie die globale “serialize”-Funktion überschreiben, die sich im Namensraum boost::serialization befindet, damit Sie IN IHREM CLIENT-CODE den Namensraum-Boost und die Namensraum-Serialisierung öffnen können!
  2. Der Stapel, um dieses Chaos zu lösen, bestand aus 10 bis 12 Funktionsaufrufen.

Ich muss ein benutzerdefiniertes Verhalten für Klassen definieren, die diese Member-Funktion nicht haben, und meine Entitäten befinden sich in verschiedenen Namespaces (und ich möchte keine globale Funktion überschreiben, die in einem Namespace definiert ist, während ich mich in einem anderen befinde).

Können Sie mir einen Tipp geben, um dieses Rätsel zu lösen?

  • Ähnliche Frage: stackoverflow.com/questions/257288

    – Johannes Schaub – litb

    25. August 2009 um 20:02 Uhr

  • @R.MartinhoFernandes Nach welcher Art von Antwort suchen Sie? Diese Antwort von Mike Kinghan geht ziemlich in die Tiefe und verwendet C++ 11-Sachen.

    – jrok

    29. Mai 2013 um 17:04 Uhr

  • @R.MartinhoFernandes Vielleicht ist dies die moderne Version, die Sie suchen?

    – Daniel Frey

    29. Mai 2013 um 19:36 Uhr

1647151813 80 Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
jrok

Hier ist eine mögliche Implementierung, die auf C++11-Features basiert. Es erkennt die Funktion korrekt, auch wenn sie vererbt wird (im Gegensatz zur Lösung in der akzeptierten Antwort, wie Mike Kinghan in seiner Antwort feststellt).

Die Funktion, auf die dieses Snippet testet, wird aufgerufen 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;
};

Verwendungszweck:

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

  • Funktioniert dies, wenn Y keine Methode namens “Serialize” hat? Ich sehe nicht, wie es einen falschen Wert zurückgeben würde, wenn die Methode “serialisieren” nicht vorhanden wäre.

    – Thalia

    20. Februar 2014 um 3:47 Uhr

  • @Collin In diesem Fall schlägt die Ersetzung des Vorlagenparameters für die erste Überladung der Prüfung fehl und wird aus dem Überladungssatz verworfen. Es greift auf das zweite zurück, das false_type zurückgibt. Dies ist aufgrund des SFINAE-Prinzips kein Compilerfehler.

    – jrok

    20. Februar 2014 um 7:57 Uhr

  • @ elios264 Gibt es nicht. Sie können ein Makro verwenden, um eine Vorlage für jede Funktion zu schreiben, die Sie überprüfen möchten.

    – jrok

    16. Oktober 2015 um 16:37 Uhr

  • Gibt es einen bestimmten Grund, warum das Argument für check vom Typ T* und nicht von T oder T& ist?

    – Schibumi

    9. November 2017 um 5:28 Uhr

  • Aber was wäre, wenn die serialize akzeptiert selbst eine Vorlage. Gibt es eine Möglichkeit zu testen serialize Existenz, ohne den genauen Typ einzugeben?

    – Hallo Engel

    9. Mai 2018 um 12:40 Uhr


1647151813 877 Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
yrp

Ich bin mir nicht sicher, ob ich Sie richtig verstehe, aber Sie können SFINAE ausnutzen, um das Vorhandensein von Funktionen zur Kompilierzeit zu erkennen. Beispiel aus meinem Code (testet, ob die Klasse die Member-Funktion size_t used_memory() const hat).

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>());
}

  • Dem Beispiel fehlt die Definition von ‘int_to_type’. Natürlich trägt es nicht zur Antwort bei, aber es bedeutet, dass die Leute Ihren Code nach einem schnellen Ausschneiden und Einfügen in Aktion sehen können.

    – Richard Corden

    18. September 2008 um 17:50 Uhr

  • Eine einfache Definition von int_to_type könnte lauten: ‘template struct int_to_type {};’. Viele Implementierungen behalten den Wert des Parameters N entweder in einer Aufzählung oder in einer statischen Integer-Konstante (template struct int_to_type { enum { value = N }; }; / template struct int_to_type { static const int value = N; })

    – David Rodríguez – Dribeas

    3. September 2009 um 22:04 Uhr

  • Nimm einfach boost::integral_constant statt int_to_type.

    – Wadim Ferderer

    28. September 2009 um 5:15 Uhr

  • @JohanLundberg Es ist ein Zeiger auf (nicht statische) Member-Funktion. Zum Beispiel, size_t(std::vector::*p)() = &std::vector::size;.

    – Kuba hat Monica nicht vergessen

    27. Juli 2015 um 14:28 Uhr

  • Ist es möglich, den Namen der Durchgangsfunktion als zweiten Vorlagenparameter zu überprüfen, dh HasMethod<Type,method_signature>?

    – kyb

    3. August 2017 um 9:00 Uhr


Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
Mike Kinghan

Die akzeptierte Antwort auf diese Frage der Introspektion von Member-Funktionen zur Kompilierzeit hat, obwohl sie zu Recht beliebt ist, einen Haken, der im folgenden Programm beobachtet werden kann:

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

Gebaut mit GCC 4.6.3, die Programmausgaben 110 – uns das mitzuteilen
T = std::shared_ptr<int> tut nicht bieten int & T::operator*() const.

Wenn Sie diesen Fallstrick noch nicht kennen, sehen Sie sich die Definition von an
std::shared_ptr<T> in der Kopfzeile <memory> wird Licht ins Dunkel bringen. In dieser Implementierung std::shared_ptr<T> wird von einer Basisklasse abgeleitet, von der es erbt operator*() const. Also die Template-Instanziierung
SFINAE<U, &U::operator*> das bedeutet “Finden” des Operators für
U = std::shared_ptr<T> wird nicht passieren, weil std::shared_ptr<T> hat keine
operator*() an sich und die Template-Instanziierung führt keine “Vererbung” durch.

Dieser Haken betrifft nicht den bekannten SFINAE-Ansatz, der “The sizeof() Trick” verwendet, um lediglich zu erkennen, ob T hat eine Mitgliedsfunktion mf (siehe zB diese Antwort und Kommentare). Aber das feststellen T::mf vorhanden ist oft (normalerweise?) nicht gut genug: Sie müssen möglicherweise auch feststellen, ob es eine gewünschte Signatur hat. Hier punktet die abgebildete Technik. Die pointerisierte Variante der gewünschten Signatur wird in einen Parameter eines Vorlagentyps eingeschrieben, der erfüllt werden muss
&T::mf damit die SFINAE-Sonde erfolgreich ist. Aber diese Vorlagen-Instanziierungstechnik gibt die falsche Antwort wann T::mf wird vererbt.

Eine sichere SFINAE-Technik für die Kompilierzeit-Introspektion von T::mf muss die Verwendung von vermeiden &T::mf innerhalb eines Vorlagenarguments, um einen Typ zu instanziieren, von dem die Auflösung der SFINAE-Funktionsvorlage abhängt. Stattdessen kann die Auflösung der SFINAE-Schablonenfunktion nur von genau relevanten Typdeklarationen abhängen, die als Argumenttypen der überladenen SFINAE-Sondenfunktion verwendet werden.

Als Antwort auf die Frage, die sich an diese Einschränkung hält, werde ich die Erkennung von Kompilierzeit veranschaulichen E T::operator*() constfür willkürlich T und E. Es gilt das gleiche Muster sinngemäß
um nach einer anderen Mitgliedsmethodensignatur zu suchen.

#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? */
};

Bei dieser Lösung funktioniert die überlastete SFINAE-Sonde test() wird “rekursiv aufgerufen”. (Natürlich wird es überhaupt nicht wirklich aufgerufen; es hat lediglich die vom Compiler aufgelösten Rückgabetypen hypothetischer Aufrufe.)

Wir müssen nach mindestens einem und höchstens zwei Informationspunkten suchen:

  • Tut T::operator*() überhaupt vorhanden? Wenn nicht, sind wir fertig.
  • Angesichts dessen T::operator*() existiert, ist seine Signatur
    E T::operator*() const?

Wir erhalten die Antworten, indem wir den Rückgabetyp eines einzelnen Aufrufs von to auswerten test(0,0). Das wird gemacht von:

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

Dieser Anruf wird möglicherweise an die aufgelöst /* SFINAE operator-exists :) */ Überlastung von test()oder es könnte zu dem auflösen /* SFINAE game over :( */ Überlast. Es kann nicht aufgelöst werden /* SFINAE operator-has-correct-sig :) */ überladen, weil dieser nur ein Argument erwartet und wir zwei übergeben.

Warum passieren wir zwei? Einfach um die Auflösung zum Ausschluss zu zwingen
/* SFINAE operator-has-correct-sig :) */. Das zweite Argument hat keine andere Bedeutung.

Dieser Aufruf an test(0,0) wird sich dazu entschließen /* SFINAE operator-exists :) */ Nur für den Fall, dass das erste Argument 0 den ersten Parametertyp dieser Überladung erfüllt, nämlich decltype(&A::operator*)mit A = T. 0 wird diesen Typ nur für den Fall erfüllen T::operator* existiert.

Nehmen wir an, der Compiler sagt Ja dazu. Dann geht es mit
/* SFINAE operator-exists :) */ und es muss den Rückgabetyp des Funktionsaufrufs bestimmen, der in diesem Fall ist decltype(test(&A::operator*)) – der Rückgabetyp eines weiteren Aufrufs an test().

Dieses Mal übergeben wir nur ein Argument, &A::operator*, von der wir jetzt wissen, dass sie existiert, sonst wären wir nicht hier. Ein Anruf bei test(&A::operator*) könnte entweder zu lösen /* SFINAE operator-has-correct-sig :) */ oder wieder zu lösen könnte /* SFINAE game over :( */. Der Anruf wird übereinstimmen
/* SFINAE operator-has-correct-sig :) */ nur für den Fall &A::operator* erfüllt den einzelnen Parametertyp dieser Überladung, nämlich E (A::*)() constmit A = T.

Der Compiler wird hier Ja sagen, wenn T::operator* die gewünschte Signatur hat und dann erneut den Rückgabetyp der Überladung auswerten muss. Keine “Rekursionen” mehr jetzt: es ist std::true_type.

Wenn der Compiler nicht wählt /* SFINAE operator-exists :) */ für den Anruf test(0,0) oder wählt nicht /* SFINAE operator-has-correct-sig :) */
für den Anruf test(&A::operator*)dann geht es in jedem Fall mit
/* SFINAE game over :( */ und der letzte Rückgabetyp ist std::false_type.

Hier ist ein Testprogramm, das die Vorlage zeigt, die die erwarteten Antworten in verschiedenen Fallbeispielen erzeugt (wieder GCC 4.6.3).

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

Gibt es neue Schwachstellen in dieser Idee? Kann es generischer gemacht werden, ohne wieder in Konflikt mit dem Haken zu geraten, den es vermeidet?

Hier sind einige Verwendungsausschnitte: *Die Eingeweide für all dies sind weiter unten

Suchen Sie nach einem Mitglied x in einer bestimmten Klasse. Könnte var, func, class, union oder enum sein:

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

Überprüfen Sie die Member-Funktion 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;

Suchen Sie nach Member-Variablen x:

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

Überprüfen Sie die Mitgliedsklasse x:

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

Suchen Sie nach einer Mitgliedsgewerkschaft x:

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

Überprüfen Sie die Mitgliedernummer x:

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

Suchen Sie nach einer Mitgliedsfunktion x unabhängig von Unterschrift:

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;

ODER

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;

Details und Kern:

/*
    - 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;
};

Makros (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)

1647151814 89 Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
Kupfer

Dies sollte ausreichen, wenn Sie den Namen der erwarteten Elementfunktion kennen. (In diesem Fall kann die Funktion bla nicht instanziiert werden, wenn es keine Member-Funktion gibt (es ist schwierig, eine Funktion zu schreiben, die trotzdem funktioniert, da es an einer teilweisen Spezialisierung der Funktion mangelt. Möglicherweise müssen Sie Klassenvorlagen verwenden.) Auch die enable-Struktur (die ist ähnlich wie enable_if) könnte auch als Vorlage für den Funktionstyp dienen, den Sie als Member haben möchten.

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

  • danke! Es ähnelt der von yrp vorgeschlagenen Lösung. Ich wusste nicht, dass Vorlagen über Member-Funktionen erstellt werden können. Das ist eine neue Funktion, die ich heute gelernt habe! … und eine neue Lektion: “Sag niemals, dass du Experte für C++ bist” 🙂

    – ugasoft

    17. September 2008 um 21:43 Uhr

1647151814 618 Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
Valentin Milea

Hier ist eine einfachere Version von Mike Kinghans Antwort. Dadurch werden geerbte Methoden erkannt. Es wird auch nach dem suchen genau Signatur (im Gegensatz zu jroks Ansatz, der Argumentkonvertierungen zulässt).

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, "");

Lauffähig Beispiel

  • danke! Es ähnelt der von yrp vorgeschlagenen Lösung. Ich wusste nicht, dass Vorlagen über Member-Funktionen erstellt werden können. Das ist eine neue Funktion, die ich heute gelernt habe! … und eine neue Lektion: “Sag niemals, dass du Experte für C++ bist” 🙂

    – ugasoft

    17. September 2008 um 21:43 Uhr

1647151814 339 Uberprufen Sie ob eine Klasse eine Member Funktion einer bestimmten Signatur
Peter Rindal

Mit c++ 20 wird dies viel einfacher. Angenommen, wir möchten eine Klasse testen T hat eine Mitgliedsfunktion void T::resize(typename T::size_type). Zum Beispiel, std::vector<U> hat eine solche Mitgliedsfunktion. Dann,

template<typename T>
concept has_resize_member_func = requires {
    typename T::size_type;
    { std::declval<T>().resize(std::declval<typename T::size_type>()) } -> std::same_as<void>;
};

und die Verwendung ist

static_assert(has_resize_member_func<std::string>, "");
static_assert(has_resize_member_func<int> == false, "");

996190cookie-checkÜberprüfen Sie, ob eine Klasse eine Member-Funktion einer bestimmten Signatur hat

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy