La resolución de sobrecarga ocurre solo cuando (a) está llamando el nombre de una función / operador, o (b) lo está enviando a un puntero (a función o función miembro) con una firma explícita.
Tampoco está ocurriendo aquí.
std::function
toma cualquier objeto que sea compatible con su firma. No toma un puntero de función específicamente. (una lambda no es una función estándar, y una función estándar no es una función lambda)
Ahora en mis variantes de funciones homebrew, para la firma R(Args...)
también acepto un R(*)(Args...)
argumento (una coincidencia exacta) exactamente por esta razón. Pero significa que eleva las firmas de "coincidencia exacta" por encima de las firmas "compatibles".
El problema central es que un conjunto de sobrecarga no es un objeto C ++. Puede nombrar un conjunto de sobrecarga, pero no puede pasarlo "nativamente".
Ahora, puede crear un conjunto de pseudo-sobrecarga de una función como esta:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
Esto crea un único objeto C ++ que puede hacer una resolución de sobrecarga en el nombre de una función.
Expandiendo las macros, obtenemos:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
lo cual es molesto de escribir. Una versión más simple, solo un poco menos útil, está aquí:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
tenemos una lambda que toma cualquier número de argumentos, luego los reenvía a ellos baz
.
Entonces:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
trabajos. Diferimos la resolución de sobrecarga en el lambda que almacenamos fun
, en lugar de pasar fun
un conjunto de sobrecarga directamente (que no puede resolver).
Ha habido al menos una propuesta para definir una operación en el lenguaje C ++ que convierte un nombre de función en un objeto de conjunto de sobrecarga. Hasta que dicha propuesta estándar esté en el estándar, la OVERLOADS_OF
macro es útil.
Podría ir un paso más allá y admitir puntero de función de conversión compatible.
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)...); };
}
};
pero eso está empezando a ponerse obtuso.
Ejemplo en 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)...); }; \
} \
}