La risoluzione del sovraccarico si verifica solo quando (a) si chiama il nome di una funzione / operatore o (b) lo si lancia su un puntatore (a funzione o funzione membro) con una firma esplicita.
Né si sta verificando qui.
std::functionaccetta qualsiasi oggetto compatibile con la sua firma. Non richiede specificamente un puntatore a funzione. (una lambda non è una funzione std e una funzione std non è una lambda)
Ora nelle mie varianti di funzione homebrew, per la firma R(Args...)accetto anche un R(*)(Args...)argomento (una corrispondenza esatta) proprio per questo motivo. Ma significa che eleva le firme di "corrispondenza esatta" al di sopra delle firme "compatibili".
Il problema principale è che un set di overload non è un oggetto C ++. È possibile nominare un set di overload, ma non è possibile passarlo in giro "nativamente".
Ora puoi creare un set di pseudo-overload di una funzione come questa:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
questo crea un singolo oggetto C ++ che può sovraccaricare la risoluzione su un nome di funzione.
Espandendo le macro, otteniamo:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
che è fastidioso da scrivere. Una versione più semplice, solo leggermente meno utile, è qui:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
abbiamo una lambda che accetta un numero qualsiasi di argomenti, quindi li inoltra perfettamente a baz.
Poi:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
lavori. Ritardiamo la risoluzione del sovraccarico nel lambda in cui archiviamo fun, invece di passare fundirettamente un set di sovraccarico (che non può risolvere).
C'è stata almeno una proposta per definire un'operazione nel linguaggio C ++ che converte un nome di funzione in un oggetto set di sovraccarico. Fino a quando una proposta del genere non è nello standard, la OVERLOADS_OFmacro è utile.
Potresti fare un ulteriore passo avanti e supportare il cast-to-compatibile-pointer-funzione.
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)...); };
}
};
ma questo sta iniziando a diventare ottuso.
Esempio dal vivo .
#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)...); }; \
} \
}