2014-12-21 13 views
6

z gcc 4.9 -std = C++ 14, starałem dokonywania wektor lambdas:Korzystanie z listy initializer z lambda w pętli zakres oparte

vector<function<void()>> v = {[]{cout << "foo";}, []{cout << "bar";}}; 
for (auto&& a: v) a(); 

I to działało całkiem dobrze. Potem próbowałem przekazując listę initializer z lambdas do zakresu oparte na wprost:

for (auto&& a: {[]{cout << "foo";}, []{cout << "bar";}}) a(); 

I mam:

error: unable to deduce 'std::initializer_list<auto>&&' from '{<lambda closure object>main()::<lambda()>{}, <lambda closure object>main()::<lambda()>{}}' 

Sądząc po wyglądzie komunikatem o błędzie, zrobiłem dziką domyślać, że prawdopodobnie jest tak dlatego, że "obiekt zamknięcia lambda" są wbudowanymi terminami w języku, a nie bezpośrednimi odpowiednikami std :: function (więc nie ma prawdziwych typów).

Jaka jest głębsza tego przyczyna? Czy może to być związane z wdrażaniem lub takie zachowanie jest podyktowane specyfikacją?

+0

[A usztywnione inicjatora ma typ] (https://www.youtube.com/watch?v=wQxj20X -tIU # t = 1799). Co wyjaśnia, dlaczego nie możesz tego wydedukować. – Borgleader

+2

@Borgleader to tutaj nie ma znaczenia, * spreparowana lista-inicjałów * w * zakresie na podstawie pętli * może zostać wydedukowana jako 'initializer_list' of * something * –

+0

@Borgleader Yes, która również przeszła mi przez myśl. Wydaje się jednak, że z komunikatu o błędzie kompilator próbuje utworzyć wpisany. To sprawia, że ​​nie jestem pewien, czy jest to błąd listy inicjalizacyjnej, która nie ma typu lub zamknięcia lambda, nie działa dobrze z nim. –

Odpowiedz

6

Każda lambda ma swój unikalny typ. Więc nie możesz budować std :: initializer_list z lambdas różnych typów.

Według C++ Standard (5.1.2 lambda wyrażenia)

3 Rodzaj lambda ekspresji (który jest również typ obiektu zamknięcia) jest unikalnym, Nienazwana zrostu typ klasy - zwany typem zamknięcia - którego właściwości opisano poniżej.

także

6 Rodzaj zamknięcia dla non-generic lambda wyrażenia bez lambda-wychwytywania ma publicznego non-wirtualny nonexplicit konwersji const funkcji do wskaźnika funkcjonować z C++ język podnośnik (7.5) posiadający ten sam parametr i typy zwrotów, co operator wywołania typu typu zamknięcia, .

+0

Zatem praktycznie zamknięcie lambda, które można przypisać do zmiennej typu 'std :: function ', ma inny typ innego zamknięcia, które można przypisać do tej samej zmiennej? –

+0

@Thanasis Papoutsidakis Zobacz mój zaktualizowany wpis. –

+0

@Jerry Coffin Nie istnieje żadna niejawna funkcja konwersji z jednej lambda do drugiej lambda. –

0

Każda wartość lambda jest niepowiązanym typem. Tak się składa, że ​​wszystkie mogą zostać przekonwertowane na std::function<void()>, ale to dlatego, że std::function będzie żądać konwersji czegokolwiek, i zadziała, kiedy będzie można je zarezerwować z sygnaturą void() i będzie można kopiować i niszczyć.

W przypadku vector znajduje się konstruktor std::initializer_list<std::function<void()>>, który jest uwzględniany na liście konstruktorów. Dopasowuje, jest próbowane i kompiluje.

Bez tego argumentu (argument listy do wektora ctor), aby dopasować, składnia {} sprawdza się w jego zawartości dla wspólnego typu. Nie ma typowego typu, więc zawiedzie.

Język nie wyszukuje każdego rodzaju i szablonu, aby znaleźć możliwy typ wspólny między dwoma (niepowiązanymi) lambdami. Nie będzie czytać w twoich myślach.

można zrobić:

using nullary = std::function<void()>; 
template<class T> 
using il=std::initializer_list<T>; 
for(auto f:il<nullary>{[]{ std::cout<<"hello";},[]{std::cout<<" world\n";}}){ 
    f(); 
} 
0

Jeden trick dowiedziałem here jest zastosowanie retrospektywne obsady. Więc z takiego narzędzia pod ręką:

template<typename T> 
struct memfun_type 
{ 
    using type = void; 
}; 

template<typename Ret, typename Class, typename... Args> 
struct memfun_type<Ret(Class::*)(Args...) const> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

template<typename F> 
typename memfun_type<decltype(&F::operator())>::type 
FFL(F const &func) 
{ // Function from lambda ! 
    return func; 
} 

ty could write coś takiego

vector<function<void()>> v = { FFL([]{cout << "foo"; }), FFL([]{cout << "bar"; }) }; 

for (auto&& a : v) a(); 
+0

Warto zauważyć, że takie podejście kończy się niepowodzeniem z użyciem ogólnych lambd C++ 14. – ildjarn

+0

@ildjarn Dlaczego tak jest? (Mam na myśli oprócz komentarza w podanym linku?) –

+1

Nie widziałem go do tej pory, ale tak, mam na myśli to samo, co R. Martinho Fernandes - nie można wziąć adresu szablon funkcji. – ildjarn

5

Każdy lamdba ma swój własny rodzaj, więc kompilator nie można wywnioskować typ initializer_list.

Trzeba powiedzieć, jaki rodzaj chcesz:

  • Dla każdego lambda:

    • Jako twój lambda nie uchwycić zmienne, można zepsucia ich do wskaźnika funkcjonować z + następująco:

      for (auto&& a: {+[]{std::cout << "foo";}, +[]{std::cout << "bar";}}) a(); 
      
    • użyciu function<void()>:

      for (auto&& a: {std::function<void()>([]{std::cout << "foo";}), 
             std::function<void()>([]{std::cout << "bar";})}) a(); 
      
  • Dla initializer_list:

    for (auto&& a: std::initializer_list<std::function<void()>>{ 
            []{std::cout << "foo";}, 
            []{std::cout << "bar";}}) a();