2013-10-01 22 views
5

Próbuję napisać funkcję podobną do std::for_each, która oprócz zwykłego użycia może również przyjąć wartość std::function<bool (param)>. Wartość false return oznacza, że ​​chcę wyjść z pętli. Poniższy kod jest tym, co dostałem do tej pory.Parametr lambda z opcjonalną wartością zwracaną

Drugie połączenie z a.visit([&](int) -> void) nie kompiluje podczas oceniania! Odwiedzającego (i). Czy można wykonać tę pracę, czy też szczerzę złe drzewo?

Używam MSVC 2010, ale chcę, aby kod był ogólnie zgodny z C++ 11.

#include <list> 
#include <string> 
#include <iostream> 

struct A 
{ 
    std::list<int> _lst; 

    template<typename _F> 
    void visit(_F visitor) 
    { 
     for(std::list<int>::const_iterator it = _lst.begin(), end = _lst.end() ; it != end ; it++) { 
      int i = *it; 
      if (std::is_void<decltype(visitor(i))>::value) { 
       visitor(i); 
      } else { 
       if (!visitor(i)) { // <----- error C2171: '!' : illegal on operands of type 'void' 
        break; 
       } 
      } 
     } 
    } 

}; 

int main(int argc, char* argv[]) 
{ 
    A a; 
    // populate a 
    for (int i = 0 ; i < 10 ; i++) { 
     a._lst.push_back(i); 
    } 

    a.visit([](int i) -> bool { 
     std::cout << i << std::endl; 
     return i < 5; 
    }); 

    a.visit([](int i) { 
     std::cout << i << std::endl; 
    }); 
} 
+0

Kod po obu stronach oddziału if musi być poprawny i możliwy do kompilacji, więc nie można wybrać na podstawie std :: is_void wewnątrz treści funkcji. – goji

+0

Dlaczego używasz nazw zaczynających się od podkreślenia i dużej litery, takich jak '_F'? –

+0

To jest konwencja, której używam dla parametrów szablonu. Myślę, że podniosłem to, patrząc na kod STL. – kylewm

Odpowiedz

6

Oto w jaki sposób zaimplementować for_almost_each; Jestem using namespace std plus aliasy typów dla celów czytelności.

#include <algorithm> 
#include <iterator> 
#include <functional> 

using namespace std; 

template<class Iter, class Func> 
Iter 
for_almost_each_impl(Iter begin, Iter end, Func func, std::true_type) 
{ 
    for (auto i = begin; i!=end; ++i) 
     if (!func(*i)) 
      return i; 
    return end; 
} 

template<class Iter, class Func> 
Iter 
for_almost_each_impl(Iter begin, Iter end, Func func, std::false_type) 
{ 
    for_each(begin, end, func); 
    return end; 
} 


template<class Iter, class Func> 
Iter for_almost_each(Iter begin, Iter end, Func func) 
{ 
    using Val = typename iterator_traits<Iter>::value_type; 
    using Res = typename result_of<Func(Val)>::type; 
    return for_almost_each_impl(begin, end, 
           func, 
           is_convertible<Res, bool>{}); 
} 

użyłem is_convertible, gdyż wydaje się bardziej sensowne niż is_same.

+0

Bardzo dziękuję, przyjąłem odpowiedź @ Troya, ponieważ było to pierwsze, ale dużo się uczę również od czytania twoich! – kylewm

+0

Zwróć też uwagę na użycie 'result_of', które zachowuje się poprawnie niezależnie od tego, jak argument ma zostać skonstruowany; Odpowiedź Troya działa tylko wtedy, gdy funktor akceptuje dosłowne "0". – DanielKO

+0

@kyle_wm: Powinieneś wybrać najlepszą odpowiedź i możesz zmienić zdanie dokładnie dlatego, że późniejsze odpowiedzi mogą być lepsze. IMO jest to przykład, w którym ta druga odpowiedź jest lepsza. – MSalters

-1

Ten lambda nie zwraca wartości, dlatego dostajesz błąd, że „gość” zwraca void:

a.visit([](int i) { 
    std::cout << i << std::endl; 
}); 

można zrobić tę pracę przez przepisywanie jako:

a.visit([](int i) -> bool { 
    std::cout << i << std::endl; 
    return true; 
}); 
+0

To nie ma znaczenia. Oba połączenia powinny działać, celem jest zmiana "wizyty", aby działała. – MSalters

4

Twoje std :: is_void musi być wykonane w czasie kompilacji i nie może być wykonane wewnątrz treści funkcji. Takie wykorzystanie funkcji przeciążenia zadziała:

#include <list> 
#include <string> 
#include <iostream> 
#include <type_traits> // missing header 

struct A 
{ 
    std::list<int> _lst; 

    // wrapper for bool returning visitor 
    template<typename _F, typename Iter> 
    bool do_visit(_F visitor, Iter it, std::true_type) 
    { 
     return visitor(*it); 
    } 

    // wrapper for non-bool returning visitor 
    template<typename _F, typename Iter> 
    bool do_visit(_F visitor, Iter it, std::false_type) 
    { 
     visitor(*it); 
     return true; 
    } 

    template<typename _F> 
    void visit(_F visitor) 
    { 
     for (auto it = _lst.begin(), end = _lst.end() ; it != end ; it++) { 
      // select correct visitor wrapper function using overloading 
      if (!do_visit(visitor, it, std::is_same<bool, decltype(visitor(0))>())) { 
       break; 
      } 
     } 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    A a; 
    // populate a 
    for (int i = 0 ; i < 10 ; i++) { 
     a._lst.push_back(i); 
    } 

    a.visit([](int i) -> bool { 
     std::cout << i << std::endl; 
     return i < 5; 
    }); 

    a.visit([](int i) { 
     std::cout << i << std::endl; 
    }); 
} 
+0

Właśnie tego nie rozumiałem. Dzięki!! – kylewm

+0

Zauważ, że 'decltype (visitor (0))' nie zadziała, gdy '0' nie jest poprawnym argumentem dla' visitor'. 'std :: result_of' istnieje z tego powodu. – DanielKO

+0

Odwiedza listę 'int' nie jest listą nieznanych typów szablonów. – goji