9

Ich untersuche eine C++ 11 Idiom, das "überlastet Lambda" nennen könnte:C++ 11 "überlastet Lambda" mit variadische Vorlage und variablen Capture

Überlastung n Funktionen mit variadische Vorlage schien mir sehr ansprechend, aber es stellte sich heraus, dass es nicht mit variabler Capture funktioniert hat: jede von [&][=] (und [this] usw., wenn in einer Elementfunktion) zu Kompilierung Versagen führen: error: no match for call to '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)> >) (char*&)' (mit meinem lokalen GCC 4.9.1 und ideone.com GCC 5.1)

Auf der anderen Seite ist der feste 2-stufige Fall didn‘ t leiden dieses Problem. (Versuchen Sie, das erste #if 0 zu #if 1 auf ideone.com zu ändern)

Irgendwelche Ideen auf, was hier geschieht? Ist das ein Compiler Bug oder weicht ich von der C++ 11/14 Spezifikation ab?

http://ideone.com/dnPqBF

#include <iostream> 
using namespace std; 

#if 0 
template <class F1, class F2> 
struct overload : F1, F2 { 
    overload(F1 f1, F2 f2) : F1(f1), F2(f2) { } 

    using F1::operator(); 
    using F2::operator(); 
}; 

template <class F1, class F2> 
auto make_overload(F1 f1, F2 f2) { 
    return overload<F1, F2>(f1, f2); 
} 
#else 
template <class... Fs> 
struct overload; 

template <class F0, class... Frest> 
struct overload<F0, Frest...> : F0, overload<Frest...> { 
    overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {} 

    using F0::operator(); 
}; 

template <> 
struct overload<> { 
    overload() {} 
}; 

template <class... Fs> 
auto make_overload(Fs... fs) { 
    return overload<Fs...>(fs...); 
} 
#endif 

#if 0 
#define CAP 
#define PRINTY() 
#else 
#define CAP y 
#define PRINTY() cout << "int y==" << y << endl 
#endif 

int main(int argc, char *argv[]) { 
    int y = 123; 

    auto f = make_overload(
     [CAP] (int x) { cout << "int x==" << x << endl; PRINTY(); }, 
     [CAP] (char *cp) { cout << "char *cp==" << cp << endl; PRINTY(); }); 
    f(argc); 
    f(argv[0]); 
} 
+0

sollten Sie ' mit Überladung :: operator(); 'in der zweiten Implementierung, [demo] (http://coliru.stacked-crooked.com/a/c908c85a29e04004) –

+0

und [deshalb Überladung mit nicht-Capturing Lambdas funktioniert] (http://coliru.stacked-crooked.com/a/dd9502d8e428c048) :-) –

+0

@Piot rSkotnicki Interessant, aber das Hinzufügen von 'overload :: operator();' hat die Situation nicht verbessert. – nodakai

Antwort

8

Überlast Auflösung funktioniert nur für Funktionen, die in einem gemeinsamen Rahmen existieren. Das bedeutet, dass die zweite Implementierung die zweite Überladung nicht findet, da Sie Funktionsaufrufoperatoren von overload<Frest...> in overload<F0, Frest...> nicht importieren.

Ein nicht erfassender Lambda-Typ definiert jedoch einen Konvertierungsoperator als Funktionszeiger mit der gleichen Signatur wie der Funktionsaufrufoperator des Lambda. Dieser Konvertierungsoperator kann über die Namenssuche gefunden werden und wird beim Entfernen des Erfassungsteils aufgerufen.

Die korrekte Implementierung, die Lambda-Ausdrücke für beide Erfassung und Nicht-Erfassung arbeitet, und das ruft immer operator() statt eines Konvertierungsoperator, wie folgt aussehen:

template <class... Fs> 
struct overload; 

template <class F0, class... Frest> 
struct overload<F0, Frest...> : F0, overload<Frest...> 
{ 
    overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {} 

    using F0::operator(); 
    using overload<Frest...>::operator(); 
}; 

template <class F0> 
struct overload<F0> : F0 
{ 
    overload(F0 f0) : F0(f0) {} 

    using F0::operator(); 
}; 

template <class... Fs> 
auto make_overload(Fs... fs) 
{ 
    return overload<Fs...>(fs...); 
} 

DEMO

+1

Beachten Sie, dass Sie, wenn Sie viele Überladungen (z. B. mehr als ein paar 100) in einer Überladungsgruppe haben möchten, die lineare Vererbung in eine ungefähr ausgeglichene binäre Vererbung ändern möchten. Und du wirst dein Design überdenken wollen, denn warum überlädst du mehr als ein paar 100 lambdas auf einmal? (Plus, die binäre Version ist mehr Code) – Yakk