2015-09-08 10 views
7

Ich habe eine Template-Funktion, wo ein Aufzählungstyp in den zugrunde liegenden Typ konvertiert wird, die funktioniert, aber ich schrieb eine Überladung, die eine ganze Zahl nehmen und sich selbst zurückgeben sollte mir ein Fehler, dass Int kein Aufzählungstyp ist. In meiner Vorlage hätte dies herausgefiltert werden sollen. Was ist falsch? Hier"Konvertierung" vom Typ zum gleichen Typ verursacht Fehler

ist der Template-Code:

template <typename TT> 
    static constexpr auto get_value(TT t) 
    -> typename std::enable_if<!std::is_enum<TT>::value, TT>::type 
    { 
     return t; 
    } 

    template <typename TT> 
    static constexpr auto get_value(TT t) 
    -> typename std::enable_if<std::is_enum<TT>::value, typename std::underlying_type<TT>::type>::type 
    { 
     return (typename std::underlying_type<TT>::type)t; 
    } 

Demo

+1

Ich weiß nicht, ob 'underlying_type' ist SFINAE freundlich, aber es gibt eine [Abhilfe] (http://coliru.stacked-crooked.com/a/e7f1dd3b75c8d9c2) für diese –

+0

Was? Ich sehe, dass es funktioniert, aber was passiert hier, dass es funktioniert? Und warum sollte 'based_type' von SFINAE nicht freundlich sein? – Adrian

+1

Die Instanziierung von 'std :: based_type :: type 'ist zurückgestellt, so dass' enable_if' zuerst fehlschlagen kann. Mit * SFINAE-freundlich * Ich meine, dass jeder Ersatz Fehler nur im unmittelbaren Kontext passiert (wenn es innerhalb 'zugrunde liegenden' selbst geschieht es ist nicht SFINAE-freundlich). –

Antwort

4

std::underlying_type<TT>::type wird in std::enable_if auch ausgewertet werden, obwohl std::is_enum<TT>::value ist false als false ist kein Fehler. Da ein Nicht-Aufzählungstyp ausgewertet wird, verursacht er einen Fehler. Wenn wir die SFINAE in die Template-Parameter verschieben, können wir die gewünschten Überladungen erhalten und trotzdem den korrekten Typ zurückgeben.

template <typename TT, typename std::enable_if<!std::is_enum<TT>::value, TT>::type* = nullptr> 
static constexpr auto get_value(TT t) -> TT 
{ 
    return t; 
} 

template <typename TT, typename std::enable_if<std::is_enum<TT>::value>::type* = nullptr> 
static constexpr auto get_value(TT t) -> typename std::underlying_type<TT>::type 
{ 
    return (typename std::underlying_type<TT>::type)t; 
} 

können Sie sehen es in diesem Arbeits Live Example

+0

Nur um sicherzustellen, dass ich das richtig verstehe, ist der zweite Template-Parameter ein unbenannter Parameter vom Typ 'void *', wenn/nicht der Enum-Typ ist (abhängig davon, was ich will), der erzeugt wird und eine Nicht-Entität nicht was ich will und würde dann abgelehnt werden? – Adrian

+0

@Adrian Wenn 'std :: enable_if' erfolgreich ist' type' ist void und wir können dann einen Zeiger des Typs auf 'nullptr' für SFINAE setzen – NathanOliver

+0

@dyp Ich habe die Antwort bearbeitet. Es sollte jetzt die eigentliche Ursache ansprechen. – NathanOliver

4

Durch den Versuch, std::underlying_type<T> mit T zu instanziiert, die nicht ein Aufzählungstyp ist, eine Anforderung verletzen, dass die Standard-T auf Template-Parameter erlegt:

§ 20.10.7.6 [meta.trans.other]/Tabelle 57:

 Template   |   Condition   |  Comments 
------------------------+---------------------------+----------------------- 
template <class T>  | T shall be an enumeration | The member typedef 
struct underlying_type; | type (7.2)    | type shall name 
         |       | the underlying type 
         |       | of T. 

Hier ist ein alternativer Ansatz, wenn man keinen zusätzlichen Template-Parameter mag:

template <typename TT> 
static constexpr auto get_value(TT t) 
    -> typename std::enable_if<!std::is_enum<TT>::value, TT>::type 
{ 
    return t; 
} 

template <typename TT> 
static constexpr auto get_value(TT t) 
    -> typename std::enable_if<std::is_enum<TT>::value 
          , std::underlying_type<TT> 
       >::type::type 
{ 
    return (typename std::underlying_type<TT>::type)t; 
} 

diese Weise wird die Instanziierung std::underlying_type<TT> aufgeschoben wird, bis die Bedingung in std::enable_if zu true auswertet, weil eine verschachtelte type Definition angefordert wird für was std::enable_if<B,T>::type zurückgibt.

DEMO

+0

Also, die Voraussetzung wird nur benötigt, wenn Sie den Typ 'std :: unterliegenden_type :: type 'bekommen, aber nicht notwendig für' std :: unterlegene_type 'selbst? Warum das? – Adrian

+1

@Adrian 'std :: based_type ' wird nicht instanziiert, wenn es nur als Typvorlagenargument verwendet wird –

Verwandte Themen