2016-10-14 1 views
2

Ich habe vor kurzem versucht, eine sfinae Typ Merkmal zu erkennen, ob eine Klasse eine bestimmte Vorlage statische Funktion namens construct enthält.Sfinae Typ Merkmal zu Template-Funktion zu erkennen funktioniert nicht mit Std :: vorwärts

Ich kam mit dieser Implementierung:

template<typename T, typename... Args> 
struct has_template_construct_helper { 
private: 
    template<typename U, typename... As> 
    static std::true_type test(decltype(&U::template construct<As...>)*); 

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

public: 
    using type = decltype(test<T, Args...>(nullptr)); 
}; 

template<typename T, typename... Args> 
using has_template_construct = typename has_template_construct_helper<T, Args...>::type; 

ich tought das in Ordnung wäre, und es war. Ich habe versucht, meine Eigenschaft mit gcc und clang wie folgt zu testen:

struct TestStruct { 
    template<typename... Args> 
    static auto construct(int a, double b, Args... args) -> decltype(std::make_tuple(a, b, args...)) { 
     return std::make_tuple(1, 2.3, std::forward<Args>(args)...); 
    } 
}; 

// didn't fire! Hurrah! 
static_assert(has_template_construct<TestStruct, std::string>::value, "Don't pass the test"); 

Es funktionierte für beide Compiler.

aber sobald ich Spedition Referenzen hinzufügen, beginnt Klirren klagen:

struct TestStruct { 
    template<typename... Args> 
    static auto construct(int a, double b, Args&&... args) -> decltype(std::make_tuple(a, b, std::forward<Args>(args)...)) 
    { 
     return std::make_tuple(1, 2.3, std::forward<Args>(args)...); 
    } 
}; 

// fires on clang :(
static_assert(has_template_construct<TestStruct, std::string>::value, "Don't pass the test"); 

Hier ist der Code-Snippet auf coliru: GCC, Clang

Meine Frage ist: die man zwischen GCC und Clang ist falsch, und wie kann ich meinen Code reparieren, damit es auf beiden Compiler funktioniert?


Okay, ich habe Dinge versucht, jetzt bin ich noch mehr verwirrt. Bei der Verwendung von std::declval funktionierte es in clang!

+0

Kommentieren Sie 'test (...)' und sehen Sie, was der tatsächliche Fehler während der Substitution ist. Das wird helfen. – skypjack

+0

Was bedeutet "funktioniert nicht mit std :: forward"? Es gibt keinen Typ, den Sie std :: forward nicht mit bekannten Ergebnissen zur Kompilierung aufrufen können. – xaxxon

+0

Ich denke, es ist ein Bug im Klang. Das Hinzufügen einer Weiterleitungsreferenz führt zu der Annahme, dass für die Konstruktmethode eine Überladung vorliegt, und daher wird der Fehler verworfen. – Arunmu

Antwort

1

Ich bin mir nicht ganz sicher, warum Ihr Code in clang ++ versagt (oder in g ++ übergeben). Aber hier ist eine einfachere Alternative.

#include <type_traits> 
#include <tuple> 
#include <string> 

template <typename... T> 
using void_t = void; 

class Stat { 
public: 
    template <typename... T> 
    static auto construct(int a, double b, T&&... t) -> 
     decltype(std::make_tuple(1, 2.3, t...)) 
    { 
     return std::make_tuple(1, 2.3, std::forward<T>(t)...); 
    } 
}; 

template <typename Class, typename... Args> 
constexpr auto does_have_construct(int) 
    -> decltype(&Class::template construct<Args...>, true) 
{ 
    return true; 
} 

template <typename Class, typename... Args> 
constexpr bool does_have_construct(long) { return false; } 

class Stat2 {}; 

int main() { 
    static_assert(does_have_construct<Stat, std::string>(0), "Nope!"); 

    return 0; 
} 

Clang ist besonders unglücklich, wenn std::forward<T> im decltype Rückgabetyp Abzug angibt. Wenn wir das entfernen, gibt es kein Problem. ABER, bin ich mir jetzt nicht sicher über die Korrektheit des Codes !!

In C++ 14 könnte man umschreiben die class Stat als:

class Stat { 
public: 
    template <typename... T> 
    static auto construct(int a, double b, T&&... t) 
    { 
     return std::make_tuple(1, 2.3, std::forward<T>(t)...); 
    } 
}; 

Wie Sie sehen können, müssen wir uns nicht nehmen den zusätzlichen Schritt, den Compiler in diesem Fall zu täuschen.

+1

hmm ... bist du sicher, vorwärts im Körper zu verwenden, aber nicht im Rückgabetyp soll arbeiten? Meistens ja, aber wenn es zu einer anderen Funktion führt, kann es versteckte Fehler oder Kompilierungsfehler verursachen? –

+1

@GuillaumeRacicot: Ja, bin mir da nicht so sicher, deshalb habe ich geschrieben "ABER ich bin mir jetzt nicht sicher über die Korrektheit des Codes !!" :). Aber der C++ 14 Weg sollte dir kein Problem bereiten. Ich wünschte wirklich, jemand könnte mit Clang über das Thema sprechen. Ich konnte nicht viel dazu finden. – Arunmu

Verwandte Themen