Eine Überlastungsauflösung tritt nur auf, wenn (a) Sie den Namen einer Funktion / eines Operators aufrufen oder (b) ihn in einen Zeiger (auf eine Funktion oder eine Elementfunktion) mit einer expliziten Signatur umwandeln.
Beides tritt hier nicht auf.
std::function
Nimmt jedes Objekt, das mit seiner Signatur kompatibel ist . Es wird kein spezieller Funktionszeiger benötigt. (Ein Lambda ist keine Standardfunktion, und eine Standardfunktion ist kein Lambda.)
In meinen Homebrew-Funktionsvarianten R(Args...)
akzeptiere ich zur Signatur R(*)(Args...)
aus genau diesem Grund auch ein Argument (eine genaue Übereinstimmung). Dies bedeutet jedoch, dass die Signaturen mit "exakter Übereinstimmung" über die "kompatiblen" Signaturen angehoben werden.
Das Hauptproblem besteht darin, dass ein Überlastungssatz kein C ++ - Objekt ist. Sie können einen Überlastungssatz benennen, ihn jedoch nicht "nativ" weitergeben.
Jetzt können Sie einen Pseudo-Überlastungssatz einer Funktion wie folgt erstellen:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
Dadurch wird ein einzelnes C ++ - Objekt erstellt, das eine Überlastungsauflösung für einen Funktionsnamen durchführen kann.
Wenn wir die Makros erweitern, erhalten wir:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
das ist nervig zu schreiben. Eine einfachere, nur etwas weniger nützliche Version finden Sie hier:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
Wir haben ein Lambda, das eine beliebige Anzahl von Argumenten akzeptiert und diese dann perfekt weiterleitet baz
.
Dann:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
funktioniert. Wir verschieben die Überlastungsauflösung auf das Lambda, in dem wir speichern fun
, anstatt zu übergebenfun
einen Überlastungssatz direkt zu übergeben (den er nicht auflösen kann).
Es gab mindestens einen Vorschlag, eine Operation in der C ++ - Sprache zu definieren, die einen Funktionsnamen in ein Überlastungssatzobjekt konvertiert. Bis ein solcher Standardvorschlag im Standard enthalten ist, ist das OVERLOADS_OF
Makro nützlich.
Sie könnten noch einen Schritt weiter gehen und den Zeiger auf einen kompatiblen Funktionszeiger unterstützen.
struct baz_overloads {
template<class...Ts>
auto operator()(Ts&&...ts)const
RETURNS( baz(std::forward<Ts>(ts)...) );
template<class R, class...Args>
using fptr = R(*)(Args...);
//TODO: SFINAE-friendly support
template<class R, class...Ts>
operator fptr<R,Ts...>() const {
return [](Ts...ts)->R { return baz(std::forward<Ts>(ts)...); };
}
};
aber das wird langsam stumpf.
Live Beispiel .
#define OVERLOADS_T(...) \
struct { \
template<class...Ts> \
auto operator()(Ts&&...ts)const \
RETURNS( __VA_ARGS__(std::forward<Ts>(ts)...) ); \
\
template<class R, class...Args> \
using fptr = R(*)(Args...); \
\
template<class R, class...Ts> \
operator fptr<R,Ts...>() const { \
return [](Ts...ts)->R { return __VA_ARGS__(std::forward<Ts>(ts)...); }; \
} \
}