2016-08-18 1 views
8

Ich bastelte, um das Beispiel auf Seite 91 von Effektivem Modern C++ zu bestätigen, und ich stieß auf etwas, das ein seltsames Problem scheint. Dieser CodeC++ noexcept Deklaration ändert Vorlage Abzug

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
    std::cout << "container version" << std::endl; 
} 

template<> 
void doStuff<int>(int& x, int& y) noexcept { 
    std::cout << "int version" << std::endl; 
} 

int main() { 
    vector<int> v1 = {1, 2, 3}; 
    vector<int> v2 = {4, 5, 6}; 
    int x = 5; 
    int y = 6; 
    doStuff(x, y); 
    doStuff(v1, v2); 
} 

gibt mir einen Fehler wie

error: request for member ‘front’ in ‘a’, which is of non-class type ‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {

So scheint es, wie die Top-Version von doStuff genannt wird, obwohl a.front() und b.front() sollte sein Verweise auf Ints zurückgeben. Wenn ich alle noexcept-Deklarationen aus dem Code entferne, erhalte ich die erwartete Ausgabe.

Dies ist mit gcc 5.4.

Was mache ich falsch?

Dank

+1

Denken Sie daran, dass Vorlagen ein compile.time Feature sind. Wenn die erste Version der 'doStuff'-Funktion definiert ist, weiß der Compiler nichts über die Spezialisierung für' int'. –

+1

Es gibt auch das Problem, dass 'doStuff' nicht deklariert wurde, wenn es zum ersten Mal in der noexcept-Spezifikation verwendet wird. – aschepler

Antwort

7

Das Problem ist, wenn die Namenssuche an dieser Stelle:

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
//           ^^^^^^^ 

wird doStuff() nur eine finden: Ihre Funktion Vorlage. Die Spezialisierung wurde noch nicht deklariert, daher wird sie nicht berücksichtigt.

Das erste, was zu tun ist, einfach Spezialisierungen zu vermeiden. Sie sind peinlich. Aber dann wäre die wirkliche Lösung, einen extra leeren Typ nur für argumentabhängige Nachschlagezwecke beizubehalten. Dies wird einen abhängigen Name den noexcept Lookup hinzufügen, die Anrufung bis Instanziierung verzögern:

namespace N { 
    struct adl { }; 

    void doStuff(adl, int& , int&) noexcept { 
     std::cout << "int version" << std::endl; 
    } 

    template<typename C> 
    void doStuff(adl, C& a, C& b) noexcept(noexcept(doStuff(adl{}, a.front(), b.front()))) { 
     std::cout << "container version" << std::endl; 
    } 
} 

template <class C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(N::adl{}, a, b))) 
{ 
    doStuff(N::adl{}, a, b); 
} 
+1

Muss die "int" Überladung wirklich zuerst sein? Ist das "doStuff" im "noexcept" -Spezifierer kein abhängiges Symbol, dh es wird nicht bis zur Instanziierung aufgelöst? – John

+0

Vielleicht überraschend wird dies für "C = std :: vector >" fehlschlagen. – aschepler

+0

@Aschepler Korrigiert. – Barry

2

Template Spezialisierungen ist nicht Überlastungen. Ihre Spezialisierung für doStuff<int> ist keine Überladung von doStuff<C>, es ist eine Spezialisierung. Wenn die Überladungsauflösung dies nicht berücksichtigt, wird die Vorlageninstanziierung dies berücksichtigen, wenn das Original durch Überladungsauflösung ausgewählt wird. Ersetzen Sie Ihre Spezialisierung mit einer Überlast (Nicht-Vorlage, wobei zwei int& s)

void doStuff(int& a, int& b) noexcept;