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::function
accetta 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 fun
direttamente 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_OF
macro è 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)...); }; \
} \
}