2016-01-12 11 views
6

Ich habe Code, um alle Elemente aus einem std::vector<int> zu entfernen, die weniger als einige int limit sind. Ich habe einige Funktionen geschrieben, die Lambda-Ausdrücke teilweise gelten:So emulieren remove_unless

auto less_than_limit = [](int limit) { 
    return [=](int elem) { 
    return limit > elem; 
    }; 
}; 

auto less_than_three = less_than_limit(3); 

Wenn ich es mit std::vector<int> v{1,2,3,4,5}; testen, ich die erwarteten Ergebnisse erhalten:

for(auto e: v) { 
    std::cout << less_than_three(e) << " "; 
} 
// 1 1 0 0 0 

Ich kann alle Elemente leicht entfernen weniger als drei:

auto remove_less_than_three = std::remove_if(std::begin(v), std::end(v), less_than_three); 

v.erase(remove_less_than_three, v.end()); 

for(auto e: v) { 
    std::cout << e << " "; 
} 
// 3 4 5 

Wie würde ich Elemente größer als oder gleich 3 mit less_than_three entfernen?

Ich versuchte less_than_three in std::not1 Einwickeln, aber Fehler bekam:

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>' 
    class unary_negate 
     ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>' 
     operator()(const typename _Predicate::argument_type& __x) const 

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<main()::<lambda(int)>::<lambda(int)> >) (int&)' 
    { return bool(_M_pred(*__it)); } 
          ^

Ich habe dann versucht std::not1(std::ref(less_than_three)), bekam aber diese Fehler:

/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >' 
    class unary_negate 
     ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >' 
     operator()(const typename _Predicate::argument_type& __x) const 
    ^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> > >) (int&)' 
    { return bool(_M_pred(*__it)); } 
          ^

Wie kann ich die Funktion in std::remove_if negieren, ohne die sich wandelnde Logik meiner Lambdas? Mit anderen Worten, wie kann ich remove_unless emulieren?

+1

ich Sie davon ausgehen, habe aus irgendeinem Grund Ihre Lambda von einem anderen Lambda des Aufrufs der offensichtliche Wahl ausgeschlossen? –

+0

@BenjaminLindley Ja, ich weiß, dies ist die klare und vernünftige Wahl. Dies ist nur für Bildungszwecke. – erip

+0

Das muss etwas mit dem Lambda-Typ zu tun haben. Sie sollten den Typ von 'less_then_three' explizit machen. 'std :: function less_than_three = weniger_than_limit (3);' – Gene

Antwort

7

std::not1 setzt Ihre Funktion Objekt von std::unary_function zufließen wird, oder zumindest die gleiche Schnittstelle zur Verfügung stellen, so dass es dann typedefs für result_type und argument_type hat.

Da ein Lambda diese nicht definiert, können Sie not1 nicht verwenden. Die naheliegende Wahl wäre, etwas ähnliches wie not1 selbst zu erstellen, aber modernere Techniken zu verwenden, um das Argument/den Ergebnistyp von allem, was es verändert, zu erkennen/zu übergeben.

Wenn Sie wirklich not1 verwenden mögen, dann ist der sinnvollste Ansatz wäre es, den Vergleich mit std::less und std::bind zu tun, der Wert angeben, auf die Sie vergleichen gehen (dh es ist im Grunde eine C++ 03 Sache , wenn Sie es verwenden, schreiben Sie alles im Stil von C++ 03).

+0

Ich möchte 'not1' nicht unbedingt verwenden. Es schien einfach die beste Option zu sein, um das Ergebnis einer Funktion zu negieren (aber offensichtlich kein Lambda). – erip

11

not1 ist etwas veraltet (und erfordert, dass der Funktor bestimmte Member typedefs, die ein Lambda eindeutig nicht bietet).

Du eine negator selbst schreiben müssen:

auto negate = [] (auto&& f) {return [f=std::forward<decltype(f)>(f)] 
      (auto&&... args) {return !f(std::forward<decltype(args)>(args)...);};}; 

Demo.

4

Sie können den Rückgabetyp des Lambda auch mit std :: function definieren.

auto remove_gte_three = std::remove_if(std::begin(v), std::end(v), std::not1(std::function<int(int)>(less_than_three))); 
+4

Die Verwendung der Funktion hier verwendet unnötigerweise das Löschen von Typen. – Columbo

+0

@Columbo Bitte ausarbeiten? Dies ist die einfachste vorgeschlagene Lösung bei weitem. –

+2

@BrianRodriguez Einfach! = Optimal. 'function' muss Typ löschen verwenden, da es keine Informationen über den Typ der Entität gibt, der außerhalb der Konstruktorvorlage gespeichert ist. – Columbo

1

Alte Art und Weise mit not1() negator (C++ 11):

// You apply the not1() negator adapter 
// to the result of less_than_three() like this: 
std::function<bool(int)> f = less_than_three; 
auto it = remove_if(begin(v), end(v), not1(f)); 

neue Art und Weise mit Lambda (C++ 14):

// Or with lambda you can negate an unary predicate. 
// Thanks to Stephan T. Lavavej 
template <typename T, typename Predicate> 
void keep_if(std::vector<T>& v, Predicate pred) 
{ 
    auto notpred = [&pred](const T& t) { return !pred(t); }; 
    v.erase(remove_if(v.begin(), v.end(), notpred), v.end()); 
} 

Verbrauch:

keep_if(v, less_than_three); 

Oder allgemeinere Lösung (C++ 14):

template <ForwardIterator I, Predicate P> 
I remove_if_not(I first, I last, P pred) 
{ 
    return std::remove_if(first, last, 
      [&](const ValueType(I)& x){ return !pred(x); }); 
} 

Verbrauch:

auto p = remove_if_not(begin(v), end(v), less_than_three); 
v.erase(p, v.end()); 
// Result: 1 2