2016-05-15 20 views
1

auf von this question, which asks about SFINAE Nach es das Beispiel gibt:SFINAE verwenden, wie man vermeiden ‚hat keinen Namen Mitglied ...‘

template<class T> 
std::string optionalToString(T* obj) 
{ 
    if (FUNCTION_EXISTS(T->toString)) 
     return obj->toString(); 
    else 
     return "toString not defined"; 
} 

Wenn jedoch ein Objekt nicht zum Beispiel eine toString() -Funktion, anstelle dieser Fall "toString nicht definiert" zurückgegeben, auch wenn wir erkennen können, ob die Funktion existiert, wird der Compiler immer noch einen Fehler, dass Objekt hat kein Element mit dem Namen "toString", vor dem Hervorheben des Zeigers toString Aufruf .

ich die gleichen Operationen auf Objekten in der Lage sein möchten, dass zu tun, die aus C++ Bibliotheken kommen, die unterschiedliche Namenskonventionen haben, zum Beispiel:

if(R_Contains_SetPosition<TemplateObject>::Value) 
{ 
TemplateObject->SetPosition(X,Y); 
} 
else if(R_Contains_setPosition<TemplateObject>::Value) 
{ 
TemplateObject->setPosition(X,Y); //TemplateObject doesn't have a setPosition defined! 
} 

Welche kann der Code bereits tun, aber der Compiler wirft ein Fehler bei allen Anweisungen, die Funktionen aufrufen, die das Objekt nicht definiert hat.

Gibt es eine Möglichkeit, den Compiler Code akzeptieren (entweder durch erneutes Schreiben oder Ändern von Compiler-Flags, vorzugsweise erstere), die eine Memberfunktion aufrufen kann (die in diesem Fall nicht ausgeführt wird trotzdem), auch wenn der Compiler weiß, dass die Member-Funktion nicht existiert?

Klarstellung:

Dies ist nicht die Frage, wie eine Funktion existiert zu erkennen. Ich habe diese Fähigkeit bereits., es fragt, wie kann ich Code schreiben, der sich auf Funktionen bezieht, die ein Objekt nicht unbedingt haben muss, ohne dass sich der Compiler darüber beschwert?

Das System ist C++ 03, daher sind experimentelle C++ 14-ähnliche Lösungen hier nicht gültig.

+0

Nein. Der gesamte Code in der Funktion wird instanziiert und muss für den angegebenen Typ gültig sein.Sie müssen sfinae und specialization verwenden, um Funktionen zu erstellen, die immer mit allen Typen funktionieren, die sie erhalten. boost :: hana hat Code, der das einfacher macht und mehr wie das aussieht, was Sie vorschlagen, aber es ist syntaktischer Zucker über sfinae. – xaxxon

+1

Er ... Inwiefern ist das nicht ein vollständiges Duplikat der Frage, die Sie mit sich selbst verknüpfen? Hast du die Antworten gelesen, bevor du diese Frage gestellt hast? Ich bin versucht, das zu beenden, aber ich gebe dir die Chance, zu antworten, falls mir etwas fehlt. – hvd

+0

Weil doing -> toString, sogar mit den vorgeschlagenen Funktionsprüfern, einen Fehler auslöst, dass die Elementfunktion nicht existiert. Ich frage nicht "Wie überprüfe ich, ob eine Funktion existiert", frage ich, "wie täusche ich den Compiler, mich nicht über die Nichtexistenz einer Funktion abzutun". – c1646091

Antwort

1

Ihr Problem ist, dass alle Zweige kompiliert werden, unabhängig davon, welche genommen wird. Also müssen alle gültigen Code enthalten.

Es gibt ein paar Möglichkeiten, um es zu umgehen. Tag-Dispatching und SFINAE sind die beiden gebräuchlichsten, aber Sie können es auch nicht "inline" machen.

Wir können jedoch nutzen sie es durch das Schreiben eines static_if Helfer inline zu tun, und C++ 14 generische lambdas mit:

namespace details { 
    template<class T, class F, class Else> 
    decltype(auto) static_if(std::true_type, T&&t, F f, Else e){ 
    return f(std::forward<T>(t)); 
    } 
    template<class T, class F, class Else> 
    decltype(auto) static_if(std::false_type, T&&t, F f, Else e){ 
    return e(std::forward<T>(t)); 
    } 
} 

template<template<class...>class Test, class T, class F, class Else> 
decltype(auto) static_if(T&&t, F f, Else e){ 
    return details::static_if(Test<T>{}, std::forward<T>(t), std::move(f), std::move(e)); 
} 

Dies ist ein static_if Helfer.

Schreiben Sie can_foo<?>. Dann können Sie es wie folgt verwenden:

auto r = 
    static_if<can_foo>(
    t, 
    [](auto&& t){return t.foo();}, 
    [](auto&&){return 7;} 
); 

C++ 14: Die C++ 11-Lösungen können nicht im Allgemeinen inline durchgeführt werden, so dass Sie auch Versand markieren könnten.

live example.

Beachten Sie, dass der Rückgabetyp static_if abhängig davon variiert, ob das Vorlagenprädikat, das für den Typ des ersten Arguments ausgewertet wurde, richtig oder falsch ist.

Dieses Argument wird an das Lambda für die wahren/falschen Zweige übergeben. In der Regel wird der Name wiederverwendet (der globalere wird ausgeblendet), da der Lambda-Eintrag "magisch" der richtige Typ ist (wird nur ausgewertet, wenn das Vorlagenprädikat für den Typ im Grunde True zurückgegeben hat).

+0

"std :: void_t" wird in meinem Compiler als kein Mitglied von Std gemeldet. Ich denke, dass der späteste, den dieser Compiler gehen kann, C++ 11 ist. Ich habe keine Ahnung, wie man den Versand markiert, und ich weiß auch nicht, wie das funktioniert. Ich gehe davon aus, dass bei der Compiler-Suche die richtige Verzweigung basierend auf dem zurückgegebenen booleschen Wert als Funktionsdefinitionssuche gewählt wird, aber ich verstehe nicht vollständig, wie es funktioniert, und ich weiß auch nicht, wie ich C++ 14 konvertieren soll in einen C++ 11 oder früheren kompatiblen Code. – c1646091

+0

@ c16 in C++ 11 Sie können es nicht inline tun, und die Antworten auf die Frage, die Sie verknüpfen, sind Ihre einzige Lösung. Tag-Dispatch-Informationen können in einer Vielzahl von Stack-Overflow-Antworten gefunden werden. Mein Code sendet Tags nur "inline", aber dafür muss C++ 14 verwendet werden. – Yakk