La résolution de surcharge se produit uniquement lorsque (a) vous appelez le nom d'une fonction / opérateur, ou (b) vous le convertissez en un pointeur (vers une fonction ou une fonction membre) avec une signature explicite.
Ni l'un ni l'autre ne se produit ici.
std::function
prend tout objet compatible avec sa signature. Il ne prend pas spécifiquement un pointeur de fonction. (une lambda n'est pas une fonction std, et une fonction std n'est pas une lambda)
Maintenant, dans mes variantes de fonction homebrew, pour la signature, R(Args...)
j'accepte également un R(*)(Args...)
argument (une correspondance exacte) pour exactement cette raison. Mais cela signifie qu'il élève les signatures de "correspondance exacte" au-dessus des signatures "compatibles".
Le problème principal est qu'un ensemble de surcharge n'est pas un objet C ++. Vous pouvez nommer un jeu de surcharge, mais vous ne pouvez pas le faire passer "nativement".
Maintenant, vous pouvez créer un ensemble de pseudo-surcharge d'une fonction comme celle-ci:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
cela crée un seul objet C ++ qui peut effectuer une résolution de surcharge sur un nom de fonction.
En développant les macros, nous obtenons:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
ce qui est ennuyeux à écrire. Une version plus simple, mais un peu moins utile, est ici:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
nous avons un lambda qui prend n'importe quel nombre d'arguments, puis parfait les transmet à baz
.
Alors:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
travaux. Nous reportons la résolution de surcharge dans le lambda dans lequel nous stockons fun
, au lieu de passer fun
directement un ensemble de surcharge (qu'il ne peut pas résoudre).
Il y a eu au moins une proposition pour définir une opération dans le langage C ++ qui convertit un nom de fonction en un objet d'ensemble de surcharge. Jusqu'à ce qu'une telle proposition standard soit dans la norme, la OVERLOADS_OF
macro est utile.
Vous pourriez aller plus loin et prendre en charge le pointeur de fonction de conversion en compatibilité.
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)...); };
}
};
mais cela commence à devenir obtus.
Exemple en direct .
#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)...); }; \
} \
}