it-swarm.com.de

C++ - Vorlagenabzug von Lambda

Ich habe eine Funktion, die zwei std::functions als Argumente verwendet. Der Parameter der zweiten Funktion hat denselben Typ wie das Ergebnis der ersten Funktion.

Ich habe eine Funktionsvorlage wie folgt geschrieben:

template<typename ResultType>
void examplFunction(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
  auto x = func();
  func2(x);
}

Ich kann es anrufen mit:

void f() {
  examplFunction<int>([]() { return 1; },   //
                      [](int v) { std::cout << "result is " << v << std::endl; });
}

Gibt es eine Möglichkeit, den <int> bei examplFunction<int> loszuwerden und den Compiler den Typ von ResultType ableiten zu lassen?

26
ErWu

Benötigen Sie tatsächlich std::function dort? std::function ist nützlich, wenn Sie Typ löschen benötigen. Mit Vorlagen können Sie es normalerweise ganz überspringen:

template<class F1, class F2>
void examplFunction(F1 func, F2 func2, decltype(func2(func()))* sfinae = nullptr) {
  auto x = func();
  func2(x);
}

Der Parameter sfinae stellt sicher, dass die Funktion nur mit Funktionen aufgerufen werden kann, sodass func2 mit dem Ergebnis von func aufgerufen werden kann.

25
Angew

Ja da ist.

template<typename ResultType>
void examplFunction_impl(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
    auto x = func();
    func2(x);
}

template<class F1, class F2>
void examplFunction(F1&& f1, F2&& f2)
{
    using ResultType = decltype(f1());
    examplFunction_impl<ResultType>(std::forward<F1>(f1), std::forward<F2>(f2));
}

Demo

In diesem Fall müssen Sie f1 ohne Argumente aufrufen, damit Sie den Rückgabetyp in der Hilfsfunktion ermitteln können. Dann rufen Sie die echte Funktion auf, während Sie diesen Rückgabetyp explizit angeben.

Sie können einige SFINAE hinzufügen, um sicherzustellen, dass diese Funktion nur an der Überlastauflösung beteiligt ist, wenn f1 tatsächlich so aufgerufen werden kann (und wenn f2 auch mit dem Rückgabewert von f1 aufgerufen werden kann).

Obwohl ich @Angew zustimmen muss, dass im angegebenen Beispielstd::function nicht erforderlich ist. Das kann in einer realen Situation natürlich anders sein.

12
Max Langhof

std::function hat einen Templated-Konstruktor (andernfalls uneingeschränkt), daher ist es nicht so einfach, ihn von einem Argumenttyp abzuleiten. Wenn diese Argumente weiterhin std::functions sein müssen, können Sie einen <int> für den Preis von zwei std::functions überspringen und den Rest durch Deduktionshilfslinien erledigen lassen:

void f() {
    examplFunction(std::function([]() { return 1; }),   //
                   std::function([](int v) { std::cout << "result is "
                                             << v << std::endl; }));
}

Eine lustige Tatsache ist, dass dies nicht immer funktioniert. Beispielsweise fehlt der aktuellen Implementierung von libc ++ ein Leitfaden für std::function, wodurch der Standard verletzt wird.

3
bipll

Angews Antwort ist großartig (und sollte die akzeptierte Antwort sein), es fehlt jedoch das kleine Detail, dass überprüft wird, ob die Abschnittsfunktion nichts zurückgibt. Dazu müssen Sie die std::is_void-Typ-Eigenschaft und std :: enable_if verwenden:

template<class F1, class F2>
void examplFunction(F1 func, F2 func2, std::enable_if_t<std::is_void_v<decltype(func2(func()))>, void*> sfinae = nullptr) {
    auto x = func();
    func2(x);
}

Wenn Sie mit den Typattributen und SFINAE nicht vertraut sind, ist dies offensichtlich viel ausführlicher und schwieriger zu lesen. Daher ist es wahrscheinlich nicht der beste Weg, wenn Sie nicht sicherstellen müssen, dass F2 void zurückgibt.

0
bobshowrocks