Frage

Für Algorithmus Template-Funktion zu schaffen Ich muss wissen, ob x oder X (und y oder Y) in der Klasse, die Template-Argument ist. Es kann sinnvoll durch, wenn meine Funktion für MFC CPoint Klasse oder GDI + PointF Klasse oder einige andere. Alle von ihnen verwenden verschiedene x in ihnen. Meine Lösung reduziert sich auf den folgenden Code sein könnte:


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

Aber es lässt sich nicht kompilieren in Visual Studio, während in der GNU C ++ kompiliert wird. Mit Visual Studio konnte ich die folgende Vorlage verwenden:


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

Aber es lässt sich nicht kompilieren in GNU C ++. Gibt es universelle Lösung?

UPD: Strukturen P1 und P2 sind hier nur zum Beispiel. Es gibt könnte alle Klassen mit unbekannten Mitgliedern.

P. S. Bitte senden Sie keine C ++ 11 Lösungen hier, weil sie offensichtlich sind und nicht relevant für die Frage.

War es hilfreich?

Lösung

Eine andere Möglichkeit ist das eine, die auf SFINAE für Ausdrücke zu. Wenn die Namen-Suche Ergebnisse in Zweideutigkeit, wird der Compiler die Vorlage ablehnen

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

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

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

Es basiert auf einer geniale Idee von jemandem zum direkten Download.

Hinweis: HasX Kontrollen für alle Daten oder Funktionselement namens x, mit beliebigem Typ. Der einzige Zweck der Mitgliedsname der Einführung ist eine mögliche Zweideutigkeit für Lookup-Mitglied-Namen zu haben - die Art des Elements nicht wichtig ist.

Andere Tipps

Hier ist eine Lösung einfacher als Johannes Schaub - litb 's ein . Es erfordert C ++ 11.

#include <type_traits>

template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Aktualisieren . Ein kurzes Beispiel und die Erklärung, wie das funktioniert

Für diese Typen:

struct A { int x; };
struct B { int y; };

Wir haben HasX<A>::value == true und HasX<B>::value == false. Mal sehen, warum.

Ersten Rückruf, die std::false_type und std::true_type haben ein static constexpr bool Mitglied namens value, die false und true gesetzt ist. Daher HasX die beiden Vorlagen über vererben dieses Mitglied. (Die erste Vorlage aus std::false_type und dem zweiten von std::true_type.)

Lassen Sie sich einfach beginnen und dann Schritt für Schritt fort, bis wir oben auf den Code zu erhalten.

1) Startpunkt:

template <typename T, typename U>
struct HasX : std::false_type { };

In diesem Fall gibt es keine Überraschung. HasX von std::false_type leitet und somit HasX<bool, double>::value == false und HasX<bool, int>::value == false

2) Säumigen U:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

Da U standardmäßig int bedeutet Has<bool> tatsächlich HasX<bool, int> und somit HasX<bool>::value == HasX<bool, int>::value == false.

3) Hinzufügen einer Spezialisierung:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };

In der Regel dank des Primärtemplats leitet HasX<T, U> von std::false_type. Es besteht jedoch eine Spezialisierung für U = int, die aus std::true_type ableitet. Daher HasX<bool, double>::value == false aber HasX<bool, int>::value == true.

Durch die Vorgabe für U, HasX<bool>::value == HasX<bool, int>::value == true.

4) decltype und eine andere Art int zu sagen:

Ein kleiner Exkurs hier aber bitte Geduld mit mir.

Im Grunde (das ist nicht ganz korrekt), decltype(expression) ergibt die Art von Ausdruck . Zum Beispiel hat 0 so geben int, decltype(0) bedeutet int. Analog 1.2 Typ double hat und somit decltype(1.2) bedeutet double.

Betrachten wir eine Funktion mit dieser Erklärung:

char func(foo, int);

Dabei gilt foo einige Klassentyp. Wenn f foo ist ein Objekt vom Typ, dann bedeutet decltype(func(f, 0)) char (die Art von func(f, 0) zurückgegeben).

Nun verwendet der Ausdruck (1.2, 0) die (integrierte) Komma-Operator, der die beiden Unterausdrücke auswertet, um (das heißt, erste 1.2 und dann 0), verwirft den ersten Wert und die Ergebnisse in der zweiten. Daher

int x = (1.2, 0);

entspricht

int x = 0;

Setzen dieses zusammen mit decltype gibt, dass decltype(1.2, 0) bedeutet int. Es gibt nichts wirklich Besonderes 1.2 oder double hier. Zum Beispiel hat true Typen bool und decltype(true, 0) bedeutet int auch.

Was ist mit einem Klasse-Typ? Für instace, was bedeutet decltype(f, 0)? Es ist natürlich zu erwarten, dass dieser int noch bedeutet, aber es könnte nicht der Fall sein. Tatsächlich könnte es eine Überlastung für den Komma-Operator ähnlich die Funktion func sein darüber ein foo und eine int nimmt und gibt einen char. In diesem Fall ist decltype(foo, 0) char.

Wie können wir die Verwendung einer Überlastung für den Komma-Operator vermeiden? Nun, es gibt keine Möglichkeit, den Komma-Operator für einen void Operanden, um eine Überlastung und wir können alles zu gieße void. Daher bedeutet decltype((void) f, 0) int. Tatsächlich wirft (void) f f von foo void die im Grunde nichts tut, aber sagen, dass der Ausdruck als mit Typ void berücksichtigt werden müssen. Dann wird das eingebaute Operator Komma verwendet wird und ((void) f, 0) Ergebnisse in 0, welchen Typ int hat. Daher decltype((void) f, 0) bedeutet int.

Ist das Guss wirklich notwendig? Nun, wenn es keine Überlastung für den Komma-Operator unter foo und int dann ist dies nicht notwendig. Wir können immer den Quellcode untersuchen, um zu sehen, ob es einen solchen Operator ist oder nicht. Wenn dies jedoch in einer Vorlage angezeigt und f hat V geben, die ein Template-Parameter ist, dann ist es nicht mehr klar (oder sogar unmöglich zu wissen), ob ein solche Überlastung für den Komma-Operator vorhanden ist oder nicht. Um generic wir werfen trotzdem.

Fazit: decltype((void) f, 0) ist eine andere Art zu sagen, int

.

5) SFINAE:

Dies ist eine ganze Wissenschaft ;-) OK, ich übertreibe ich, aber es ist nicht ganz einfach nicht. Also werde ich die Erklärung auf das absolute Minimum halten.

SFINAE steht für Substitution Ausfall ist nicht ein Fehler. Es bedeutet, dass, wenn ein Template-Parameter durch einen Typ ersetzt wird, ein illegales C ++ Code erscheinen könnte, aber in einigen circunstances , anstatt Kompilierung eines Abbruchs der Compiler einfach den Code säumige ignoriert, als ob es nicht da war . Mal sehen, wie es in unserem Fall gilt:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Auch hier decltype((void) T::x, 0) ist eine andere Art int zu sagen, aber mit dem Vorteil der SFINAE.

Wenn T mit einem Typ ersetzt wird, könnte ein ungültiges Konstrukt erscheinen. Zum Beispiel ist nicht gültig bool::x C ++, so Substituieren T mit bool in T::x ergibt ein ungültiges Konstrukt. Unter dem SFINAE Prinzip, wird der Compiler den Code nicht ablehnen, es einfach ignoriert (oder Teile davon) es. Genauer gesagt, wie wir seenHasX<bool> bedeutet eigentlich HasX<bool, int> haben. Die Spezialisierung für U = int ausgewählt, sondern werden sollte, während es instanziiert wird, der Compiler findet bool::x und ignoriert die Template-Spezialisierung zusammen, als ob sie nicht vorhanden.

An diesem Punkt ist der Code essencially die gleiche wie im Fall (2) oben, wo nur die primäre Vorlage vorhanden ist. Daher HasX<bool, int>::value == false.

Das gleiche Argument für bool verwendet hält für B seit B::x ein ungültiges Konstrukt (B hat kein Mitglied x). Allerdings ist A::x OK und der Compiler sieht kein Problem in der Spezialisierung für U = int Instanziieren (oder, genauer gesagt, für U = decltype((void) A::x, 0)). Daher HasX<A>::value == true.

6) Unnaming U:

Nun, Blick auf den Code in (5) wieder sehen wir, dass der Name U nicht überall verwendet wird, aber in seiner Erklärung (typename U). Wir können dann die zweite Vorlage Argument Unname und wir erhalten den Code am Anfang der Post gezeigt.

habe ich hier von einer Frage umgeleitet die hat wurde als Duplikat dieser einen geschlossen. Ich weiß, es ist ein alter Thread, aber ich wollte nur eine Alternative vorzuschlagen (einfacher?) Implementierung, die mit C ++ 11 arbeitet. Angenommen, wir, ob eine bestimmte Klasse eine Membervariable namens id überprüfen möchten hat:

#include <type_traits>

template<typename T, typename = void>
struct has_id : std::false_type { };

template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };

Das ist es. Und hier ist, wie wäre es verwendet werden ( Live-Beispiel ) :

#include <iostream>

using namespace std;

struct X { int id; };
struct Y { int foo; };

int main()
{
    cout << boolalpha;
    cout << has_id<X>::value << endl;
    cout << has_id<Y>::value << endl;
}

Die Dinge können sich noch einfacher mit ein paar Makros vorgenommen werden:

#define DEFINE_MEMBER_CHECKER(member) \
    template<typename T, typename V = bool> \
    struct has_ ## member : false_type { }; \
    template<typename T> \
    struct has_ ## member<T, \
        typename enable_if< \
            !is_same<decltype(declval<T>().member), void>::value, \
            bool \
            >::type \
        > : true_type { };

#define HAS_MEMBER(C, member) \
    has_ ## member<C>::value

Welche diese Weise verwendet werden:

using namespace std;

struct X { int id; };
struct Y { int foo; };

DEFINE_MEMBER_CHECKER(foo)

int main()
{
    cout << boolalpha;
    cout << HAS_MEMBER(X, foo) << endl;
    cout << HAS_MEMBER(Y, foo) << endl;
}

UPDATE:. Ich habe vor kurzem einig mehr mit dem Code getan, was ich in meiner ersten Antwort geschrieben, so dass ich dies für Änderungen / Ergänzungen zu berücksichtigen Aktualisierung

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

Nach Mitglied x in einer Klasse. Könnte sein, var, func, Klasse, Vereinigung oder Enum:

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

Nach Elementfunktion 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;

Nach Membervariable x:

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

Nach Mitglied Klasse x:

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

Nach Mitgliedsverband x:

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

Nach Mitglied Enum x:

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

Überprüfen Sie für jede Memberfunktion x unabhängig von Signatur:

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.
*/

template <typename... Args> struct ambiguate : public Args... {};

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)

Boost.ConceptTraits bietet zwischen anderen einige Makros Typ Merkmale definieren, wie beispielsweise BOOST_TT_EXT_DEFINE_HAS_MEMBER(name), welches eine Art von Merkmal Form definiert:

has_member_##name<T>

Das gibt true, wenn T ein Mitglied Typ mit dem Namen hat. Beachten Sie jedoch, dass dies nicht Referenzart Mitglieder erkennen.

Du Fall wird es genug sein, um in einer Header-Datei hinzufügen

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

und prüfen Sie wie folgt

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

Die verwendete Technik ist die gleiche wie die auf einigen der vorhergehenden Antworten erläutert.

Leider ist diese Bibliothek nicht mehr aufrechterhalten. Nun, da C ++ 0x wird nicht Konzept beinhaltet, diese Bibliothek zusammen mit SFINAE ist ein perfekter Ersatz mit den meisten der Konzepte zu arbeiten.

Warum Sie Spezialisierung wie diese nicht verwenden:

struct P1 {int x; };
struct P2 {int X; };

template<class P> 
bool Check_x(P p) { return true; }

template<> 
bool Check_x<P2>(P2 p) { return false; }

Die zweite Antwort (litb ist) auf das zeigt, wie ein Mitglied erkennen:

Ist es möglich, eine Vorlage zu schreiben zu überprüfen für eine Existenz Funktion?

Warum Sie nicht nur Vorlage Spezialisierungen von Check_x erstellen?

template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

Heck, wenn ich denke, davon. Wenn Sie nur zwei Typen haben, warum brauchen Sie auch Vorlagen für das?

Sind die Funktionen (x, x, y, Y) von einer abstrakten Basisklasse, oder könnten sie Refactoring so sein? Wenn so dass Sie die SUPERSUBCLASS () Makro von Modern C ++ Design, zusammen mit Ideen aus der Antwort auf diese Frage verwenden können:

Compiler-Typ Abhängiges Senden

Wir können bei der Kompilierung erhalten: 0 - not_member, 1 - is_object, 2 - is_function für jede erforderliche Klasse und Mitglied - Objekt oder eine Funktion: http://ideone.com / Fjm9u5

#include <iostream>
#include <type_traits>

#define IS_MEMBER(T1, M)    \
struct {        \
    struct verystrangename1 { bool M; };    \
    template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
    \
    enum return_t { not_member, is_object, is_function }; \
    template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member;  }  \
    template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; }   \
    template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; }   \
    constexpr operator return_t() { return what_member<T1>(); } \
}

struct t {
    int aaa;
    float bbb;
    void func() {}
};

// Can't be in function
IS_MEMBER(t, aaa) is_aaa_member_of_t;
IS_MEMBER(t, ccc) is_ccc_member_of_t;
IS_MEMBER(t, func) is_func_member_of_t;

// known at compile time
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
static constexpr int const_is_func_member_of_t = is_func_member_of_t;

int main() {        
    std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
        "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
        "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
        "is func member of t = " << is_func_member_of_t << std::endl << 
        std::endl;

    return 0;
}

Ergebnis:

0 - not_member, 1 - is_object, 2 - is_function 

is aaa member of t = 1
is ccc member of t = 0
is func member of t = 2

Für Klasse / Struktur:

struct t {
    int aaa;
    float bbb;
    void func() {}
};
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top