2013-01-07 12 views
17

Mam działający szablon funkcji, który wywołuje lambda.Jak korzystać z variadic idealnego przekazywania do lambda?

Chciałbym uogólnić ten szablon funkcji, aby przyjmować argumenty variadyczne i przekazywać je idealnie do lambda, ale mam problem z uzyskaniem tego kodu do kompilacji.

Używam gcc 4.7.2.

UPDATE

Korzystanie sugestię R. Martinho Fernandes, spojrzałem w górę błąd w Bugzilla - it does look like a bug that's been around for a while. Jeśli ktoś wie o obejściu problemu (szukam go teraz), proszę napisać odpowiedź.

BŁĘDY

junk.cpp: In lambda function: 
junk.cpp:32:68: error: parameter packs not expanded with ‘...’: 
junk.cpp:32:68: note:   ‘args’ 
junk.cpp: In instantiation of ‘std::pair<std::basic_string<char>, typename T::Lambda> MP(const string&, M, Args&& ...) [with T = Integer; M = int (Integer::*)()const; Args = {}; typename T::Lambda = std::function<std::function<int()>(const Integer&)>; std::string = std::basic_string<char>]’: 
junk.cpp:47:42: required from here 
junk.cpp:34:2: error: using invalid field ‘MP(const string&, M, Args&& ...)::<lambda(const T&)>::__args’ 
make: *** [junk] Error 1 

KOD

#include <functional> 
#include <iostream> 
#include <map> 

struct Integer 
{ 
    typedef std::function<int()>       Function; 
    typedef std::function<Function(Integer const& inst)> Lambda; 

    virtual int getInt() const = 0; 
}; 

struct IntImpl : public Integer 
{ 
    virtual int getInt() const { return 42; } 
}; 

typedef std::function<int()>        IntFunction; 
typedef std::function<IntFunction(Integer const& inst)> IntLambda; 

#define WONT_COMPILE 

template<typename T,typename M,typename... Args> 
std::pair<std::string,typename T::Lambda> 
MP(std::string const& str, M method, Args&&... args) 
{ 
#ifdef WONT_COMPILE 
    return std::make_pair(str, 
     [=](T const& inst) 
     { 
      // COMPILE ERROR (Line 32) on next line 
      return std::bind(method, std::cref(inst), std::forward<Args>(args)...); 
     } 
    ); 
#else 
    return std::make_pair(str, 
     [method](T const& inst) 
     { 
      return std::bind(method, std::cref(inst)); 
     } 
    ); 
#endif 
} 

std::map<std::string,IntLambda> const g_intTbl = 
{ 
    MP<Integer>("getInt", &Integer::getInt) 
}; 

int 
main(int argv, char* argc[]) 
{ 
    IntImpl x; 
    std::cerr << g_intTbl.find("getInt")->second(x)() << std::endl; 
} 
+1

Niektóre z tych błędów wydają się występować, ponieważ 'std :: naprzód (args ...)' 'powinna być std :: naprzód (args) ...' –

+0

@AndreiTita +1 Ty - to pomaga - ja Nadal dostaję błędy - OP został zaktualizowany – kfmfe04

+1

No cóż, nie wiem, czy wolno ci rozszerzać pakiety variadic w przechwytach lambda (sprawdzi to za chwilę), ale rozwiązaniem byłoby użycie pełnego przechwytywania jak '[=]'. –

Odpowiedz

7

Wydaje się to być błąd kompilatora (prosimy zgłosić go, jeśli jeszcze nie jest). Średnia mówi:

ekspansji pakietu zawiera wzorzec i elipsy, której instancji wytwarza zero lub więcej dawałaby wzoru na liście (opisane poniżej). Forma wzorca zależy od kontekstu, w którym następuje rozwinięcie, od . ekspansje opakowań mogą wystąpić w następujących sytuacjach:

- [...]
- W przechwytywania liście (5.1.2); wzór jest przechwytywaniem.
- [...]

Poprawi to twój kod.

Dopóki nie otrzymasz kompilatora, który poradzi sobie z tym, będziesz przechwytywał wszystko jako obejście, z [=].

+0

Heh Właśnie napisałem to samo (ale masz ładniejsze standardowe cytaty). W szczególności jest to błąd gcc - MSVC (listopad CTP) i Clang 3.2 akceptują rozszerzenia argumentów pack w przechwytywaniach lambda. –

+0

+1 za świecenie światła na problemie: jeśli ktoś zna obejście gcc, proszę post - ty – kfmfe04

11

Jeśli ktoś zna obejście (jestem kopanie wokół jednego teraz), prosimy ogłaszać odpowiedzi

wpadłem dokładnie ten sam problem i znaleźć obejście. To trochę opóźniona odpowiedź, mam nadzieję, że znalazłeś rozwiązanie w międzyczasie, ale tutaj tak jest (może to być przynajmniej przydatne innym).

Chodzi o to, aby zmienić parametry lambda, tak że również przyjmuje takie same argumenty co zmiennej liczbie argumentów funkcji zewnętrznego (np. [](int) {} staje [](int, Args&&... args) {}) i bind lambda do zmiennej liczbie argumentów argumentów Zewnętrzna funkcji. Gdy to zrobisz, nie ma już problemu z przekazywaniem argumentów variadic wewnątrz lambda.

Podsumowując:

template<typename... Args> 
std::function<void (int)> foo(Args&&... args) { 
    return [&](int bar) { 
        // COMPILER BUG: doesn't work with GCC 4.7 despite the capture 
        doSomething(bar, std::forward<Args>(args)...); 
       }; 
} 

template<typename... Args> 
std::function<void (int)> foo(Args&&... args) { 
    return std::bind([](int bar, Args&&... args) { 
          // now this works with GCC 4.7 
          doSomething(bar, std::forward<Args>(args)...); 
         }, 
        std::placeholders::_1, std::forward<Args>(args)...); 
} 

Jasne jest to brzydki hack, ale przynajmniej można nadal uzyskać zamierzoną funkcjonalność nawet gdy utkniesz z kompilatora buggy.

+0

+1 ty dla zamieszczania swoich ustaleń - spróbuję tego. – kfmfe04

+0

Czy to działa tak, jak napisano? Myślę, że musisz zmienić sygnaturę zwracanej wartości, aby uwzględnić fakt, że funkcja std :: przyjmuje parametry "int" i "Args && ..." jako parametry. –

+0

@Sean Nie, kod, który wysłałem działa tak, jak jest. Nie ma potrzeby zmiany typu zwracanej wartości 'foo', ponieważ część' Args && ... 'jest * związana * przed zwróceniem funktora. W tym właśnie chodzi o "bind": częściowe zastosowanie argumentów związanego funktora. – syam

Powiązane problemy