2013-03-05 5 views
30

Ich arbeite an der Aktualisierung einiger C++ - Code, um die neue Funktionalität in C++ 11 zu nutzen. Ich habe eine Merkmalsklasse mit einigen Funktionen, die grundlegende Typen zurückgibt, die meistens, aber nicht immer, einen konstanten Ausdruck zurückgeben würden. Ich würde gerne verschiedene Dinge machen, je nachdem ob die Funktion constexpr ist oder nicht. Ich kam mit dem folgenden Ansatz auf:Erkennung von conexpr mit SFINAE

template<typename Trait> 
struct test 
{ 
    template<int Value = Trait::f()> 
    static std::true_type do_call(int){ return std::true_type(); } 

    static std::false_type do_call(...){ return std::false_type(); } 

    static bool call(){ return do_call(0); } 
}; 

struct trait 
{ 
    static int f(){ return 15; } 
}; 

struct ctrait 
{ 
    static constexpr int f(){ return 20; } 
}; 

int main() 
{ 
    std::cout << "regular: " << test<trait>::call() << std::endl; 
    std::cout << "constexpr: " << test<ctrait>::call() << std::endl; 
} 

Die zusätzlichen int/... Parameter gibt es so, dass, wenn beide Funktionen nach verfügbar sind SFINAE, die erste durch Überlastung Auflösung gewählt wird.

Kompilieren und Ausführen dieser mit Clang 3.2 zeigt:

regular: 0 
constexpr: 1 

Also das scheint zu funktionieren, aber ich würde gerne wissen, ob der Code legal ist C++ 11. Besonders, da ich davon überzeugt bin, dass sich die Regeln für SFINAE geändert haben.

+0

Interessantes Problem. Ich habe eine Antwort geschrieben, von der ich dachte, dass sie legal ist, aber dann habe ich eine [modifizierte Version] (http://liveworkspace.org/code/SWmBI$5) geschrieben, die laut meiner Antwort und dieser Aussage gleichermaßen gültig sein sollte Kompiliert keinen Compiler. Also werde ich die Antwort nicht einreichen, aber ich bin sehr neugierig. –

+3

Zugehörig: [Ist is_contexpr in C++ 11 möglich?] (Http://stackoverflow.com/questions/13299394/is-is-constexpr-possible-in-c11). Siehe auch [Aufruf von constexpr im Standardvorlagenargument] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-de-default-template-argument). –

+0

@AndyProwl: Ich bin neugierig, könnten Sie vielleicht Ihre Lösung auf liveworkspace.org oder einer anderen ähnlichen Website posten? –

Antwort

13

HINWEIS:I opened a question here darüber, ob OPs Code tatsächlich gültig ist. Mein neu geschriebenes Beispiel wird in jedem Fall funktionieren.


aber ich würde der Code wissen, ob ++ legal C 11

ist

Es ist, obwohl das Standard-Template-Argument ein wenig ungewöhnlich betrachtet werden kann. Ich persönlich mag den folgenden Stil besser, die ähnlich ist, wie Sie (sprich: I) schreibt einen Zug zu check for a function's existence, nur einen nicht-Typ Template-Parameter verwenden und die decltype Weglassen:

#include <type_traits> 

namespace detail{ 
template<int> struct sfinae_true : std::true_type{}; 
template<class T> 
sfinae_true<(T::f(), 0)> check(int); 
template<class> 
std::false_type check(...); 
} // detail:: 

template<class T> 
struct has_constexpr_f : decltype(detail::check<T>(0)){}; 

Live example.


Erläuterung Zeit ~

Ihre ursprüngliche Code funktioniert weil ein Standardpunkt der Vorlage Argument der Instanziierung der Punkt instantiati ist auf seiner Funktionsschablone, die in Ihrem Fall in main bedeutet, so kann es nicht früher als das ersetzt werden.

§14.6.4.1 [temp.point] p2

Wenn eine Funktion template [...] in einer Art und Weise genannt wird, die die Definition eines Standardargument dieser Funktion Vorlage verwendet [...], der Punkt der Instanziierung der Standard Argument ist der Instanziierungspunkt der Funktionsvorlage [...].

Danach sind es nur die üblichen SFINAE-Regeln.


† Atleast ich denke schon, es ist nicht ganz klar in der Norm.

+0

Dieses Zitat aus dem Standard löscht alle meine Zweifel, danke. –

+0

+1 auch für die alternative Lösung –

+0

leider denke ich nicht, dass es so klar ist. Ein "Standard-Template-Argument" konnte für Funktionen in C++ 03 nicht angegeben werden, aber dieser Text war auch dort vorhanden. Der Text spricht nur über Standardargumente für Funktionen und nicht für Klassenvorlagen. Das lässt mich glauben, dass es nicht über Vorlagenargumente, sondern Funktionsargumente spricht. –

1

Gefordert von @ Marshall-Clow, habe ich eine etwas mehr generische Version eines Typmerkmals für die Erkennung constexpr zusammengestellt. Ich habe es auf std::invoke_result modelliert, aber da constexpr von den Eingaben abhängt, sind die Vorlagenargumente für die übergebenen Werte und nicht für die Typen.

Es ist etwas begrenzt, da die Vorlage Args nur eine limited set of types sein können, und sie sind alle const, wenn sie den Methodenaufruf erhalten. Sie können problemlos eine constexpr Wrapper-Methode testen, wenn Sie andere Typen benötigen, oder nicht konstante Werte für einen Referenzparameter.

Also etwas mehr von einer Übung und Demonstration als tatsächlich nützlichen Code.

Und die Verwendung von template<auto F, auto... Args> macht es C++ 17-only, benötigen gcc 7 oder clang 4. MSVC 14.10.25017 kann es nicht kompilieren.

namespace constexpr_traits { 

namespace detail { 

// Call the provided method with the provided args. 
// This gives us a non-type template parameter for void-returning F. 
// This wouldn't be needed if "auto = F(Args...)" was a valid template 
// parameter for void-returning F. 
template<auto F, auto... Args> 
constexpr void* constexpr_caller() { 
    F(Args...); 
    return nullptr; 
} 

// Takes a parameter with elipsis conversion, so will never be selected 
// when another viable overload is present 
template<auto F, auto... Args> 
constexpr bool is_constexpr(...) { return false; } 

// Fails substitution if constexpr_caller<F, Args...>() can't be 
// called in constexpr context 
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()> 
constexpr bool is_constexpr(int) { return true; } 

} 

template<auto F, auto... Args> 
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {}; 

} 

Live demo with use-cases on wandbox