2016-04-19 19 views
1

Ich versuche, eine Funktion, die einen gemeinsamen Zeiger auf einige Funktor akzeptiert. Mit manuell erstellten Funktoren gibt es keine Probleme, aber mit Lambda gibt es. Ich weiß, dass ich decltype mit Lambda nicht verwenden kann - jede neue Lambda-Deklaration erstellt einen neuen Typ. Im Moment schreibe ich:Smart Zeiger auf Lambda

auto lambda = [](int a, float b)->int 
{ 
    return 42; 
}; 
using LambdaType = decltype(lambda); 

shared_ptr<LambdaType> ptr{ new LambdaType{ lambda } }; 

Es funktioniert, aber sieht hässlich aus. Außerdem gibt es einen Kopierkonstruktor Aufruf! Gibt es eine Möglichkeit zu vereinfachen?

+4

Sie versuchen, lambdas zusammen mit intelligenten Zeigern in einer Weise zu verwenden, die wirklich keinen Sinn macht, das wäre so viel einfacher, wenn Sie 'std :: function' stattdessen verwenden würden. Sie * verwenden * den intelligenten Zeiger IMO als Ressourcen-Wrapper anstatt nur einen einfachen selbstlöschenden Zeiger.Wie für die "extra indirection" von "std :: function", wird das wirklich ein Engpass in Ihrem Code sein? Es ist nicht mehr eine zusätzliche Umleitung, als mit intelligenten Zeigern wirklich, und mit 'std :: function' haben Sie eine schönere und viel sauberere Syntax, sowohl beim Einrichten als auch beim Verwenden/Aufrufen der Funktion. –

+1

http://coliru.stacked-crooked.com/a/997216714e0e7d74 –

+3

Oh, und die Verwendung von 'std :: function' beschränkt Sie nicht nur auf lambdas, was ist, wenn die Leute Ihren Code pflegen oder anderweitig Ihren Code verwenden , möchte eine Nicht-Lambda-Funktion verwenden? Mit eigenen "Smart Function Pointer" -Typen ist das nicht möglich. Da jedes Lambda einen eindeutigen Typ hat, kann der gemeinsame Zeiger außerdem nur für das spezifische Lambda und kein anderes Lambda mit der gleichen Signatur verwendet werden. –

Antwort

1

Lambdas sind lediglich automatisch geschriebene aufrufbare Objekte, um einfachen Code einfach zu machen. Wenn Sie etwas über ihr automatisches Standardspeicherverhalten hinaus möchten, schreiben Sie den Typ, den sie selbst schreiben.

Es ist illegal, einen Lambda-Typ in einem unbewerteten Kontext zu haben. In einem evaluierten Kontext erstellt es ein Lambda im automatischen Speicher. Sie wollen es im kostenlosen Geschäft. Dies erfordert zumindest logisch eine Kopie.

Eine schreckliche Hack Einbeziehung der unevaluierten Kontextregel verletzen, sizeof/alignof, aligned_storage_t, Platzierung neuer, möglicherweise unbegrenzt Kompilierung Rekursion (oder vielleicht auch eines mit einem static_assert), Zeiger auf lokale Variablen und der Aliasing-Konstruktor der gemeinsamen ptr Rückkehr und wenn Anrufer wahnsinnigen Code schreiben müssen, könnte dies den Aufruf von copy/move verhindern. Aber es ist eine schlechte Idee und einfach aufrufbare Objekte zu verwenden ist einfacher.

Natürlich akzeptiert das Kopieren/Verschieben es trivial. Aber an diesem Punkt verwenden Sie einfach std::function, es sei denn, Sie benötigen etwas wie Varargs.

Sie geben an, dass Benutzer nicht gezwungen werden sollen, std::function zu verwenden; aber std::function würde implizit ein kompatibles Lambda in sich selbst umwandeln.

Wenn Sie bereit sind, eine Kopie zu übernehmen, können wir dies tun:

template<class T> 
std::shared_ptr<std::decay_t<T>> 
auto_shared(T&& t) { 
    return std::make_shared<std::decay_t<T>>(std::forward<T>(t)); 
} 

dann auto ptr = auto_shared([x=0]()mutable{ return x++; }); ist ein nicht-Typ-gelöschten freigegebenen Zeiger auf eine Zählung Lambda. Das Lambda wird kopiert (also verschoben) in den gemeinsamen Speicher. Wenn Sie diese Kopie vermeiden möchten, kann der Client ein manuelles Funktionsobjekt schreiben und make_shared<X>(ctor_args) darauf aufrufen.

Es gibt keinen vernünftigen Weg, einen lambdas-Typ von seiner Konstruktion in C++ zu trennen.

3

Sie könnten std::function als Typ verwenden.

+0

Ja, ich weiß. Wie ich geschrieben habe, gibt es keine Probleme mit "üblichen" Funktoren. Aber Lambda ist ein bisschen anders. Und 'std :: function' erzeugt eine weitere Indirektionsstufe. – nikitablack

+1

Nun, Sie brauchen Indirection für Typ löschen und Sie müssen Typ löschen, um den Ad-hoc-Typ eines Lambda zu verbergen. Sie werden also nicht viel besser machen als 'std :: function', das für Lambdas und Funktoren identisch funktioniert. – Useless

1

Wenn Sie etwas in Lambda fangen, wird es algorithmisch dasselbe wie std::function, also verwenden Sie es frei. Außerdem implementiert std::function die Speicherverwaltung für erfasste Werte, sodass die Verwendung von std::shared_ptr nicht erforderlich ist.

Wenn Sie nichts zu fangen, ist Lambda umwandelbar in einfachen Funktionszeiger:

int(*ptr)(int,int) = [](int a, int b) -> int { 
    return a+b; 
}; 

Funktionen werden statisch zugewiesen und auf jeden Fall nicht gelöscht werden sollen. Sie brauchen also nicht std::shared_ptr

+0

Es ist nicht konvertierbar, wenn es ein Capture hat. – nikitablack