9

śledztwo C++ 11 idiom, który można nazwać "przeciążony lambda":C++ 11 "przeciążony lambda" o zmiennej liczbie argumentów szablonu i zmiennym wychwytywania

Przeciążanie n Funkcje z szablonem variadic wydawały mi się bardzo atrakcyjne, ale okazało się, że nie działały z przechwytywaniem zmiennych: dowolne z [&][=][y][&y] (i [this] etc jeśli w funkcji składowej) prowadzić do niewydolności kompilacji: error: no match for call to '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)> >) (char*&)' (z mojego lokalnego GCC 4.9.1 i ideone.com GCC 5.1)

Z drugiej strony, stały 2-ary przypadek wstałem t cierpi na ten problem. (Spróbuj zmienić pierwszy kod #if 0 na #if 1 na ideone.com)

Jakieś pomysły na to, co się tutaj dzieje? Czy jest to błąd kompilatora, czy też odstępuję od specyfikacji C++ 11/14?

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

powinno być ' używając przeciążenia :: operator(); "w drugiej implementacji, [demo] (http://coliru.stacked-crooked.com/a/c908c85a29e04004) –

+0

i [dlatego przeciążanie niezatłoczonymi lambdami działa] (http://coliru.stacked-crooked.com/a/dd9502d8e428c048) :-) –

+0

@ Piot rSkotnicki Interesujące, ale dodanie 'using overload :: operator();' nie poprawiło sytuacji. – nodakai

Odpowiedz

8

rozdzielczości przeciążenie działa tylko dla funkcji, które istnieją we wspólnym zakresie. Oznacza to, że druga implementacja nie może znaleźć drugiego przeciążenia, ponieważ nie importujesz operatorów wywołań funkcji z overload<Frest...> do overload<F0, Frest...>.

Jednak nie przechwytujący typ lambda definiuje operatora konwersji do wskaźnika funkcji z tym samym podpisem, co operator wywołania funkcji lambda. Ten operator konwersji można znaleźć za pomocą wyszukiwania nazw i jest to wywoływane po usunięciu przechwytywania.

Prawidłowa realizacja, który działa zarówno dla przechwytywania i zakaz robienia lambdy i że zawsze zwraca operator() zamiast operatora konwersji powinien wyglądać następująco:

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

Należy zauważyć, że jeśli chcesz wiele przeciążeń (powiedzmy, więcej niż kilka 100) w jednym zestawie przeciążenia, będziesz chciał zmienić dziedziczenie liniowe na zgrubne dziedziczenie binarne. I będziesz chciał ponownie przemyśleć swój projekt, ponieważ dlaczego przeciążasz więcej niż kilka 100 lambdas na raz? (Plus, wersja binarna to więcej kodu) – Yakk

Powiązane problemy