una tipos programacion parametros llamar funciones funcion estructura ejemplos dev con como arreglos c++ c++11 language-lawyer sfinae

c++ - tipos - SFINAE en funciones con parámetros predeterminados-función libre vs operador()



funciones en dev c++ (2)

Cuando se pasa la función libre como un argumento, se realiza la conversión de la función al puntero. Cuando eso sucede, el argumento predeterminado ( que no forma parte del tipo de función ) desaparece. Ahora es un puntero a una función que toma dos parámetros y, como tal, solo puede pasar una comprobación SFINAE.

El tipo de la lambda no sufre tal ajuste. La expresión no evaluada debe implicar una resolución de sobrecarga para el operator() , y eso encuentra la declaración con el argumento predeterminado, lo que permite que se use en una llamada con un solo argumento.

Cuando la lambda sin capturas se ve obligada a sufrir una conversión a un puntero de función (por ejemplo, func(+defaultLambda); cortesía de @YSC), la ambigüedad desaparece, por el mismo motivo.

Estaba jugando con esta respuesta para investigar cómo maneja las funciones con los parámetros predeterminados. Para mi sorpresa, los resultados son diferentes para las funciones libres y el operator() :

template <typename F> auto func(F f) -> decltype(f(42)) { int a = 51; return f(51); } template <typename F> auto func(F f) -> decltype(f(42, 42)) { int a = 0; int b = 10; return f(a, b); } int defaultFree(int a, int b = 0) { return a; } auto defaultLambda = [](int a, int b = 0) { return a; }; int foo() { return func(defaultFree); //return func(defaultLambda); }

Enlace de Godbolt

La versión de func(defaultFree) anterior compila mientras ambas plantillas de func están disponibles. Como se esperaba, elige el segundo porque los parámetros predeterminados no se consideran parte de la firma de la función. De hecho, la eliminación de la segunda plantilla de func provoca un error de compilación.

Sin embargo, func(defaultLambda) no se compila debido a la ambigüedad: ambas plantillas de func coinciden. Eliminar cualquiera de los dos hace que esta versión se compile.

(Lo mismo sucede si escribes manualmente una struct con un operator() similar operator() , por supuesto. Las últimas gcc , clang y MSVC coinciden en ello).

¿Cuál es la razón por la que el parámetro predeterminado se considera dentro del contexto SFINAE no evaluado para operator() pero no para la función libre?


Un nombre de función no es el nombre de un objeto C ++.

En su lugar, cuando utiliza un nombre de función, se producen un montón de conversiones. La resolución de sobrecarga se realiza en función del contexto de conversión o de conversión (implícita o explícita) y se genera un puntero.

Los argumentos predeterminados en una función son parte de la resolución de sobrecarga. Nunca se pasan como parte de un tipo de puntero de función.

Puede crear un contenedor simple que convierte el nombre de una función en un objeto de función:

#define RETURNS(...) / noexcept(noexcept(__VA_ARGS__)) / -> decltype(__VA_ARGS__) / { return __VA_ARGS__; } #define OVERLOADS_OF(...) / [](auto&&...args) / RETURNS( __VA_ARGS__( decltype(args)(args)... ) )

Con esto, puedes modificar tu código:

return func(OVERLOADS_OF(defaultFree));

y obtenga los argumentos predeterminados que deben ser considerados por func y SFINAE para generar ambigüedad.

Ahora, OVERLOADS_OF(defaultFree) es un objeto de función que SFINAE comprueba si sus argumentos se pueden pasar a un llamable llamado defaultFree . Esto permite que se pase 1 argumento.

Los objetos de función no son funciones. Un lambda es un objeto de función, como lo es el tipo de retorno de OVERLOADS_OF . Los objetos de función se pueden pasar y su operator() sobrecargado operator() considerar; Pueden recordar sus parámetros por defecto, hacer SFINAE, etc.

Así que cuando pasas la lambda, ambas posibilidades son legales. Cuando se pasa una función, se convierte en un puntero a la función de la llamada no-default-argument a la función, y eso no acepta de forma inequívoca 1 parámetro.

Para solucionar su problema, debe hacer que una sobrecarga se vea mejor que la otra.

Un enfoque es utilizar ... :

namespace impl { template <typename F> auto func(F f,...) -> decltype(f(42)) { int a = 51; return f(51); } template <typename F> auto func(F f, int) -> decltype(f(42, 42)) { int a = 0; int b = 10; return f(a, b); } } template <typename F> auto func(F f) -> decltype( impl::func(f, 0) ) { return impl::func(f, 0); }

el truco es que se prefiere int a ... cuando pasas 0 .

También podría ser más explícito, y generar rasgos como "se puede llamar con 1 argumento", "se puede llamar con 2 argumentos", luego declare que el caso de 1 argumento solo está habilitado cuando se le puede llamar con 1 pero no con 2 argumentos .

También hay técnicas de orden de resolución de sobrecarga basadas en despacho de etiquetas.