web-dev-qa-db-ger.com

Übergeben Sie den Lambda-Ausdruck an das Lambda-Argument c ++ 11

Ich möchte so etwas machen:

int main()
{
    auto f = [/*some variables*/](/*take lambda function*/)
    {/*something with lambda function*/};

    f([/*other variables*/](/*variables to be decided by f()*/)
    {/*something with variables*/});
}

Ich weiß, dass es möglich ist, ein Lambda an eine Funktion sowie an ein Lambda zu übergeben.

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};

    f([](int i) -> double
    {return 0.0;});
}

Folgendes funktioniert jedoch nicht (sobald ich die Bereichsvariablen in [x] addiere)

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;}

    f([x](int i) -> double    //[x] does not work
    {return 0.0;});
}

was gibt den fehler:

error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list
        argument types are: (lambda [](int)->double)
        object type is: lambda [](double (*)(int))->double

hätte jemand eine Idee, wie man das beheben kann oder wie man es umgehen kann? Ich benutze den Intel-Compiler .__

Vielen Dank

19
Couchy311

Zu Ihrer Frage gibt es ein paar Dinge zu klären. Die erste davon ist Was ist ein Lambda?

Ein Lambda-Ausdruck ist ein einfacher Ausdruck, aus dem der Compiler einen eindeutigen Typ generiert, der nicht benannt werden kann, und gleichzeitig eine Instanz des Typs generiert. Wenn Sie: [](int i) { std::cout << i; } schreiben, generiert der Compiler einen Typ für Sie, der ungefähr so ​​lautet:

struct __lambda_unique_name {
   void operator()(int i) const { std::cout << i; }
};

Wie Sie sehen, handelt es sich um nicht eine Funktion, sondern um einen Typ, der operator() als const-Memberfunktion implementiert. Wenn das Lambda ein Capture erstellt hat, generiert der Compiler Code, um den Wert/die Referenzen zu erfassen.

Als Eckfall für Lambdas wie oben, bei denen kein Zustand erfasst wird, ermöglicht die Sprache eine Konvertierung vom Lambda-Typ in einen Zeiger, um mit der Signatur des operator() (minus dem this-Teil) zu funktionieren, also dem Lambda Oben kann implizit in einen Zeiger auf eine Funktion umgewandelt werden, die int nimmt und nichts zurückgibt:

void (*f)(int) = [](int i) { std::cout << i; }

Nun, da die Grundlagen festgelegt wurden, haben Sie in Ihrem Code dieses Lambda:

auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;};

Die Regeln für Parameter für Funktionen (die auch für Lambdas gelten) bestimmen, dass ein Argument nicht vom Typ function sein darf. Das Argument für das Lambda fällt also in einen Zeiger auf function ab (auf dieselbe Weise wie ein Argument vom Typ Array zerfällt in einen Zeigertyp):

auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};

Zu einem späteren Zeitpunkt versuchen Sie, ein Lambda zu übergeben, das ein Capture als Argument hat. Da ein Capture vorhanden ist, gilt die Sonderregel nicht und das Lambda kann nicht in einen Zeiger auf eine Funktion konvertiert werden, die den angezeigten Compiler-Fehler liefert.

Im aktuellen Standard haben Sie zwei Möglichkeiten. Sie können die Löschung von Typen verwenden, um den genauen Typ der aufrufbaren Entität aus der Signatur zu entfernen:

auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};

Da eine std::function<double(int)> mit einer beliebigen callable -Einheit mit der entsprechenden Signatur initialisiert werden kann, akzeptiert dies die im folgenden Code aufgeführten Lambdas zu den Kosten einer Typlöschung, die normalerweise eine dynamische Zuweisung und einen dynamischen Versand impliziert.

Alternativ können Sie den syntaktischen Zucker fallen lassen und das erste Lambda-Äquivalent manuell rollen, aber generisch machen. Wenn das Lambda einfach ist, kann dies eine gültige Option sein:

struct mylambda {
   template <typename F>
   double operator()(F fn) const {
      fn(0); return 0.0;
   }
} f;
// then use the non-lambda as you tried:
f([x](int i) -> double {return 0.0;});

Wenn Sie geduldig genug sind, können Sie schließlich auf C++ 14 warten, wo (wahrscheinlich noch nicht ratifiziert) polymorphe Lambdas unterstützt wird, was die Erstellung der obigen Klasse vereinfacht:

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above

Versuchen Sie es mit der Funktion std :::

#include <functional>
int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](std::function<double(int)> func) -> double
             {func(0); return 0.0;};

    f([x](int i) -> double {return 0.0;});
}
5
DanielKO

Möglicherweise müssen Sie einfach die Kugel beißen und Ihre eigenen Funktoren implementieren, wie wir es im dunklen Zeitalter getan haben:

struct F {
    int x;
    int y;

    F(int x_, int y_) : x(x_), y(y_) {}

    template <typename G>
    double operator() (G&& g) const {
        g(0);
        return 0.0;
    }
};

#include <iostream>

int main()
{
    int x = 0;
    int y = 0;
    auto f = F(x, y);

    f([x](int i){return 0.0;});
    f([](int i){std::cout << i << std::endl;});
}

Das sollte Sie weiter machen, bis Ihr Compiler generische C++ 14-Lambdas unterstützt.

3
Casey

Sie könnten etwas wie das Folgende versuchen, wenn Sie beispielsweise die Art des Lambda vorher kennen:

int main()
{
    int x = 0, y = 0;

    auto f = [x]( int i )->double {
        return (double)x;
    };

    auto f2 = [x,y]( decltype(f) func )->double {
        return func( 0 );
    };

    f2( f );

    return 0;
}

Alternativ können Sie die <functional>-Bibliothek für eine allgemeinere Lösung verwenden, zum Beispiel:

auto f = [x,y]( std::function<double(int)> func ) { /* Do stuff */ };
1
Thomas Russell

Sie können ein Fang-Lambda angeben, diese Lösung hat jedoch ihre Einschränkungen:

#include <new>

#include <utility>

namespace
{

template <typename F, int I, typename L, typename R, typename ...A>
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
  std::declval<F>()(std::declval<A>()...))))
{
  static L l_(std::forward<L>(l));
  static bool full;

  if (full)
  {
    l_.~L();

    new (static_cast<void*>(&l_)) L(std::forward<L>(l));
  }
  else
  {
    full = true;
  }

  return [](A... args) noexcept(noexcept(
      std::declval<F>()(std::forward<A>(args)...))) -> R
    {
      return l_(std::forward<A>(args)...);
    };
}

}

template <typename F, int I = 0, typename L>
inline F cify(L&& l)
{
  return cify<F, I>(std::forward<L>(l), F());
}


int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};

    f(cify<double(*)(int i)>([x](int i) -> double    //works now
    {return 0.0;}));
}

Klicken Sie auf für ein Arbeitsbeispiel.

0
user1095108