2016-04-12 20 views
6

Ein paar Fragen für C++ 11 Experten.g ++ und clang ++ unterschiedliches Verhalten bei SFINAE und SFINAE Fehler

Ich kämpfe mit SFINAE und bin auf einen seltsamen Fall gestoßen, in dem sich g ++ (4.9.2) und clang ++ (3.5.0) anders verhalten.

Ich habe den folgenden Beispielcode vorbereitet. Es tut mir leid, aber ich kann es nicht wesentlich prägnanter machen.

#include <string> 
#include <iostream> 
#include <typeinfo> 
#include <type_traits> 

template <typename X> 
class foo 
{ 
    private: 
     template <typename R> 
     using enableIfIsInt 
     = typename std::enable_if<std::is_same<X, int>::value, R>::type; 

    public: 
     foo() 
     { } 

     template <typename R = void> 
     enableIfIsInt<R> bar() 
      { std::cout << "bar: is int\n"; } 

     void bar() 
     { 
     std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" 
      << typeid(enableIfIsInt<void>).name() << "}\n"; 
     } 
}; 


int main() 
{ 
    foo<long> fl; 
    foo<int> fi; 

    fl.bar(); 
    fi.bar(); 

    return 0; 
} 

Meine Idee war, eine Vorlage foo<X> Klasse zu erstellen, die (via SFINAE) ein Verfahren, in einer oder in einer anderen Art und Weise auf dem X Template-Argumente je definieren.

Das Programm kompiliert und mit g ++ 4.9.2 aber Klirren ++ 3.5.0 gibt die folgenden Fehler

test.cpp:13:36: error: no type named 'type' in 
     'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable 
     this declaration 
     = typename std::enable_if<std::is_same<X, int>::value, R>::type; 
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~ 
test.cpp:26:23: note: in instantiation of template type 
     alias 'enableIfIsInt' requested here 
      << typeid(enableIfIsInt<void>).name() << "}\n"; 
        ^
test.cpp:36:7: note: in instantiation of member function 
     'foo<long>::bar' requested here 
    fl.bar(); 
    ^
1 error generated. 

Ich nehme an, dass Recht Klirren ist ++, aber meine erste Frage an C++ 11 Experten ist: Wer nicht wahr? g ++ oder clang ++?

über die g ++ erzeugen Programmausgabe, es ist die folgenden

bar: isn't int; is [i]{v} 

so g ++ scheint die fl.bar(); Anweisung zu ignorieren.

Nun ein wenig ändern: i die zweite Version von foo<X>::bar() auf diese Weise modifizieren

void bar() 
    { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; } 

die std::enable_if innerhalb der Funktion Gräuel zu löschen. Nun sind beide g ++ und Klirren ++ kompilieren, ohne Probleme und die Ausgabe für beide kompilierte Versionen des Programms ist

bar: isn't int; is [l] 
bar: isn't int; is [i] 

Also, meine zweite Frage ist: was ich falsch mache? Warum, in dem int Fall, erhalte ich nicht die "is int" Version von foo<X>::bar()?

Haben Sie Geduld mit mir, wenn ich etwas töricht mache: Ich versuche C++ 11 zu lernen.

Und Entschuldigung für mein schlechtes Englisch.

+1

Haben Sie versucht, dies mit einer neueren Klirren Version (wie 3.8) und ein neueres gcc (wie 5.3)? Könnte aufschlussreich sein ... –

+0

Meist minimierte Repro: http://coliru.stacked-crooked.com/a/713fbbbca7e8b8c5. Ich beschuldige Caching. –

+2

Berichtet als https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70642 –

Antwort

6

Der Clang-Fehler kommt nicht vom Substitutionsfehler. Es kommt von hier:

void bar() 
    { 
    std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" 
     << typeid(enableIfIsInt<void>).name() << "}\n"; // <== 
    } 

enableIfIsInt<void> ist nicht im unmittelbaren Kontext, das ist ein harter Ausfall für X nicht int. Sie können diesen Ausdruck in diesem Kontext einfach nicht verwenden.

Sobald Sie das entfernen - die Nicht-Vorlage bar() wird immer aufgerufen. Das liegt daran, dass beide Funktionen äquivalente Übereinstimmungen sind und Nicht-Vorlagen den Vorlagen in Überladungsauflösung vorgezogen werden.

So ist die wirkliche Lösung ist tag-Dispatching zu verwenden:

void bar() { bar(std::is_same<X, int>{}); } 

void bar(std::true_type) { 
    std::cout << "bar: is int\n"; 
} 

void bar(std::false_type) { 
    std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; 
} 

mit den beide Compiler glücklich ergeben:

bar: isn't int; is [l] 
bar: is int 
+0

was ich nicht konzentriert hatte, war, dass Nicht-Vorlagen zu Vorlagen in Überladungsauflösung bevorzugt werden; Vielen Dank. – max66