2016-10-12 4 views
3

Während auf einem C++ 11-Typ eingestellten Arbeits habe ich versucht, diese Funktion zu implementieren (abgespeckte auf das Minimum):Warum schlägt der Rückgabetyp decltype für die rekursive Vorlage fehl, während die Rückgabetypableitung problemlos funktioniert?

constexpr auto test() -> bool; 

template <typename T, typename... Rest> 
constexpr auto test() -> decltype(test<Rest...>()) 
{ 
    return {}; 
} 

Sowohl gcc und Klappern Drossel auf diesem. Clang sagt:

test.cpp:54:40: error: 'Rest' does not refer to a value 
constexpr auto test() -> decltype(test<Rest...>()) 
           ^

gcc klagt:

test.cpp:54:44: error: expected primary-expression before ‘...’ token 
constexpr auto test() -> decltype(test<Rest...>()) 

Ich denke, das liegt daran, dass die variadische Version von Test ist nicht einmal vollständig erklärt, wenn die decltype es betrachtet.

Allerdings, wenn ich Rückgabetyp Abzug in C++ verwenden 14, das kompiliert just fine:

constexpr auto test() -> bool; 

template <typename T, typename... Rest> 
constexpr auto test() 
{ 
    return test<Rest...>(); 
} 

Scheint, wie test ausreichend hier erklärt gilt.

Ich frage mich, warum das nicht für die decltype Variante funktioniert? Selbst wenn ich den C++ 14-Support aktiviere?

PS: Es stellt sich heraus, dass ich nicht wirklich nennen kann sogar die C++ 14-Funktion, so vielleicht die ganze Sache verpfuscht ist ...

+0

Ist eine reine C++ 14 Lösung in Ordnung für dich, oder würdest du lieber eine C++ 11 eins machen? – TartanLlama

+0

Eines der Probleme ist, dass 'test <>()' nicht das gleiche wie 'test()' ist, so dass der letzte rekursive Aufruf fehlerhaft ist. – Holt

+0

@TartanLlama Ich habe eine Lösung basierend auf Typvorlagen für C++ 11. Ich bin gerade auf dem Weg darüber gestolpert und würde gerne verstehen, was hier vor sich geht. Ich würde auch interessieren, wie man das in C++ 11 oder C++ 14 "richtig" macht. – Rumburak

Antwort

5

Ein Problem ist, dass Ihre erste test Überlastung ist keine Funktion Vorlage , kann also nicht mit test<>() Syntax aufgerufen werden.

Allerdings funktioniert decltype nicht wirklich mit rekursiven variadic Funktionen wie diesem, da der Rückgabetyp Teil der Deklaration der Funktion ist, so dass Überladung nicht deklariert wird, wenn der Name nachgeschlagen wird.

Sie können dieses Problem umgehen, in C++ 11 durch Template-Klassen statt:

template <typename... Ts> 
struct test; 

template <typename T, typename... Ts> 
struct test<T,Ts...> { 
    static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); } 
}; 

template <> 
struct test<> { 
    static bool value() { return true; } 
}; 

Live demo


In C++ 14 können Sie die erste Überladung machen eine einzige Vorlage nehmen Parameter und der zweite Nimm zwei und ein Parameterpaket:

template <typename T> 
constexpr auto test() -> bool { return true; } 

template <typename T, typename U, typename... Rest> 
constexpr auto test() 
{ 
    return test<U,Rest...>(); 
} 

Live demo

+0

Sie benötigen nicht einmal den zusätzlichen Parameter 'U', oder? – Rumburak

+0

@Rumburak Ja, sonst wird der Anruf mehrdeutig sein. – TartanLlama

+0

Ah, richtig. Ich habe eine Version mit Funktionsparametern versucht ('template consExpr auto test (T, Rest ... Ruhe) {return test (Rest ...);}'). Das funktioniert ohne das U. Ich denke, ich muss die Reihenfolge der Überladungsauflösung genauer verstehen ... – Rumburak

3

Während die Template-Funktion selbst deklariert wird, wurde die Template-Funktion selbst nicht deklariert. Daher ist es für den deklarativen Rückgabetyp nicht sichtbar.

Sie können dies mit ADL beheben. Wenn Ihre Template-Funktion ein Argument aus demselben Namespace wie Ihre Template-Funktion übernimmt, wird die Suche nach dem Rückgabetyp bereit, die Template-Funktion selbst zu betrachten. Dies liegt daran, dass Vorlagen nach ihrer Rückgabetypsignatur suchen, indem sie den Kontext verwenden, bevor sie deklariert wurden, und, wobei ADL für jeden der Parameter verwendet wird.

template<class...Ts> struct types {}; 

namespace bob{ 
    struct mytag{}; 
    constexpr auto test(mytag, types<>) -> bool{ return true; } 

    template <typename T, typename... Rest> 
    constexpr auto test(mytag, types<T,Rest...>) 
    -> decltype(test(mytag{}, types<Rest...>{})) 
    { 
    return test(mytag{},types<Rest...>{}); 
    } 
} 
template<class...Ts> 
constexpr auto test() 
->decltype(bob::test(bob::mytag{}, types<Ts...>{})){ 
    return bob::test(bob::mytag{}, types<Ts...>{}); 
} 

Sie können für mytag je nach Compiler constexpr types(){}; und ähnliche müssen.

Dies behebt auch die Tatsache, dass test<> in Ihrem ursprünglichen Code illegal ist. Funktionen sind viel besser mit Typen übergeben (Tag Vorlage verpackt) Wert meiner Erfahrung nach. Überladen ist freundlicher.

Verwandte Themen