2013-01-07 17 views
17

Bir lambda çağıran bir çalışma işlevi şablonum var.Bir lambdada variadic mükemmel iletmeyi nasıl kullanırım?

Variadic argümanları almak ve bunları lambda'ya mükemmel bir şekilde iletmek için bu işlev şablonunu genelleştirmek istiyorum, ancak bu kodu derlemek için sorun yaşıyorum.

gcc 4.7.2 kullanıyorum.

GÜNCELLEME

R. Martinho Fernandes öneri kullanarak

, ben bugzilla'da hatayı baktı - it does look like a bug that's been around for a while. Eğer bir çözümden haberi varsa (şimdi bir tane için kazıyorum), lütfen bir cevap gönderin.

HATALAR

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 Bu (zaten değilse lütfen bildirin) derleyici bir hata gibi görünen

#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

bu hataların bazıları (... args) '' std ileri :: (args) olmalıdır std ileri :: 'Çünkü ortaya görünmektedir ...' –

+0

@AndreiTita 1 ty - yardımcı olur - Ben Yine de hatalar alıyorum - OP güncellendi – kfmfe04

+1

Peki, lambda yakalamalarda varadik paketleri genişletmek için izinli olup olmadığını bilmiyorum (bir anda bunu kontrol edecek), ama bir geçici çözüm gibi tam bir yakalama kullanmak olacaktır '[=]'. –

cevap

7

. Standart söyler:

bir listede Desenin sıfır veya daha fazla örneklemesi üreten örneğinin bunun bir desen ve üç nokta, oluşan bir paket genişleme (aşağıda tarif edilmiştir). Desenin formu, genişlemenin oluştuğu bağlamda 'a bağlıdır. Paketi açılımları aşağıdaki bağlamlarda ortaya çıkabilir:

- [...]
- Bir yakalama listesinde (5.1.2) ise; desen bir yakalamadır.
- [...]

Bu kod doğru hale getirir.

Bunu işleyebilecek bir derleyici elde edinceye kadar, [=] ile her şeyi bir geçici çözüm olarak yakalayacaksınız.

+0

Heh Aynı şeyi yazmak üzereydim (ama daha güzel standart tırnaklarınız var). Özellikle gcc bug - MSVC (Kasım CTP) ve Clang 3.2 lambda yakalamalarda argüman paketi genişletme kabul. –

+0

+1 problemi aydınlatmak için +1: Eğer herkes bir gcc çözümü bulursa, lütfen bulgularınızı yayınlamak için lütfen – kfmfe04

11

herkes bir geçici çözüm (şimdi biri için etrafında kazıyorum) bilirse, bir cevap

Ben aynı sorunla karşılaştık ve geçici bir çözüm buldu gönderin. Geç bir cevap, umarım bu arada bir çözüm bulursunuz, ama işte burada (başkalarına da faydalı olabilir). o da dış fonksiyonu (örn. [](int) {}[](int, Args&&... args) {} olur) ve dış işlevin variadic argümanları bind lambda aynı variadic argümanları kabul ettiğini, böylece

fikri lambda parametrelerini değiştirmektir. Bu yapıldıktan sonra, lambda içindeki değişkenli argümanları iletmede sorun yoktur.

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)...); 
} 

Tabii bu çirkin kesmek, ancak bir adamcağız derleyici ile sıkışmış yaparken en azından hala bile istenen işlevselliği elde edebilirsiniz:

Özetle.

+0

+ 1 ty gönderin - Bunu deneyeceğim – kfmfe04

+0

Bu, yazılı olarak çalışıyor mu? Std :: işlevinin int 've 'Args && ...' parametrelerini aldığını hesaba katmak için dönüş değerinin imzasını değiştirmeniz gerektiğini düşünüyorum. –

+0

@Sean Hayır, yayınladığım kod çalışıyor. Funger'i döndürmeden önce 'Args && ...' kısmı * bağlı * olduğundan, 'foo'nun dönüş değerini değiştirmeye gerek yoktur. Bu aslında 'bağlama' bütün noktası: bağlı functor argümanlarının kısmi uygulanması. – syam