¿El argumento de plantilla (la firma) de std :: function no es parte de su tipo?

Dado el siguiente código, ¿cuál es el motivo de la ambigüedad? ¿Puedo eludirlo o tendré que guardar los moldes explícitos (molestos)?

#include  using namespace std; int a(const function& f) { return f(); } int a(const function& f) { return f(0); } int x() { return 22; } int y(int) { return 44; } int main() { a(x); // Call is ambiguous. a(y); // Call is ambiguous. a((function)x); // Works. a((function)y); // Works. return 0; } 

Curiosamente, si comulgo la función a a() con el parámetro function y llamo a(x) en mi main, la comstackción falla correctamente debido a la falta de coincidencia de tipos entre x y la function argumento function de la única función a a() disponible. Si el comstackdor falla en ese caso, ¿por qué habría ambigüedad cuando las dos funciones a a() están presentes?

Lo he intentado con VS2010 y g ++ v. 4.5. Ambos me dan exactamente la misma ambigüedad.

    El problema es que tanto la function como la function son construibles desde la misma función. Esto es lo que parece la statement de constructor de std::function en VS2010:

     template function(_Fx _Func, typename _Not_integral< !_Is_integral<_Fx>::value, int>::_Type = 0); 

    Ignorando la parte SFINAE, es construible a partir de casi cualquier cosa.
    std::/boost::function emplea una técnica llamada borrado de tipo , para permitir el paso de objetos / funciones arbitrarias, siempre que satisfagan la firma al ser llamados. Una desventaja de esto es que se obtiene un error en la parte más profunda de la implementación (donde se llama a la función guardada) cuando se suministra un objeto que no se puede llamar como lo quiere la firma, en lugar de en el constructor.


    El problema se puede ilustrar con esta pequeña clase:

     template class myfunc{ public: template myfunc(Func a_func){ // ... } }; 

    Ahora, cuando el comstackdor busca funciones válidas para el conjunto de sobrecarga, intenta convertir los argumentos si no existe una función de ajuste perfecto. La conversión puede ocurrir a través del constructor del parámetro de la función, o mediante un operador de conversión del argumento dado a la función. En nuestro caso, es el primero.
    El comstackdor prueba la primera sobrecarga de a . Para hacerlo viable, necesita hacer una conversión. Para convertir un int(*)() a un myfunc , prueba el constructor de myfunc . Al ser una plantilla que requiere algo, la conversión tiene éxito naturalmente.
    Ahora intenta lo mismo con la segunda sobrecarga. El constructor sigue siendo el mismo y sigue tomando todo lo que se le dio, la conversión también funciona.
    Al quedar con 2 funciones en el conjunto de sobrecarga, el comstackdor es un panda triste y no sabe qué hacer, por lo que simplemente dice que la llamada es ambigua.


    Entonces, al final, la parte Signature de la plantilla pertenece al tipo al hacer declaraciones / definiciones, pero no cuando se quiere construir un objeto.


    Editar :
    Con toda mi atención al responder la pregunta del título, me olvidé por completo de tu segunda pregunta. 🙁

    ¿Puedo eludirlo o tendré que guardar los moldes explícitos (molestos)?

    Afaik, tienes 3 opciones.

    • Mantener el reparto
    • Haga un objeto de function del tipo apropiado y pase ese

      function fx = x; function fy = y; a(fx); a(fy);

    • Oculta el casting tedioso en una función y usa TMP para obtener la firma correcta

    La versión de TMP (metaprogtwigción de plantillas) es bastante prolija y tiene un código repetitivo, pero oculta el lanzamiento del cliente. Aquí puede encontrar una versión de ejemplo, que se basa en la get_signature get_signature que está parcialmente especializada en tipos de punteros de función (y proporciona un buen ejemplo de cómo la coincidencia de patrones puede funcionar en C ++):

     template struct get_signature; template struct get_signature{ typedef R type(); }; template struct get_signature{ typedef R type(A1); }; 

    Por supuesto, esto debe ampliarse para la cantidad de argumentos que quiera admitir, pero eso se hace una vez y luego se entierra en un "get_signature.h" . 🙂

    Otra opción que considero descartada inmediatamente fue SFINAE, que introduciría incluso más código repetitivo que la versión TMP.

    Entonces, sí, esas son las opciones que yo sé. Espero que uno de ellos trabaje para ti. 🙂

    He visto esta pregunta demasiadas veces. libc ++ ahora comstack este código sin ambigüedad (como una extensión conforme).

    Actualización vencida

    Esta “extensión” demostró ser suficientemente popular como para ser estandarizada en C ++ 14 (aunque no fui personalmente responsable de hacer ese trabajo).

    En retrospectiva, no recibí esta extensión exactamente correcta. A principios de este mes (05/05/2015) el comité votó en el número 2420 de LWG, que efectivamente cambia la definición de Callable, de modo que si la std::function tiene un tipo de retorno void , ignorará el tipo de devolución del funtor envuelto, pero aún así de lo contrario, considérelo invocable si todo lo demás coincide, en lugar de considerarlo no invocable .

    Este ajuste posterior a C ++ 14 no afecta este ejemplo en particular ya que los tipos de devolución involucrados son consistentemente int .

    Aquí hay un ejemplo de cómo ajustar std::function en una clase que verifica la invobilidad de sus parámetros de constructor:

     template struct check_function; template struct check_function: public std::function { template::value || std::is_convertible< decltype(std::declval()(std::declval()...)), R>::value>::type> check_function(T &&t): std::function(std::forward(t)) { } }; 

    Use esto:

     int a(check_function f) { return f(); } int a(check_function f) { return f(0); } int x() { return 22; } int y(int) { return 44; } int main() { a(x); a(y); } 

    Tenga en cuenta que esto no es lo mismo que sobrecargar en la firma de la función, ya que trata los tipos de argumento convertible (y retorno) como equivalentes. Para una sobrecarga exacta, esto debería funcionar:

     template struct check_function_exact; template struct check_function_exact: public std::function { template::value>::type> check_function_exact(T &&t): std::function(std::forward(t)) { } }; 

    std::function tiene un controlador de conversión que toma un tipo arbitrario (es decir, algo que no sea una T ). Claro, en este caso, ese ctor daría lugar a un error de desajuste de tipo, pero el comstackdor no llega tan lejos: la llamada es ambigua simplemente porque existe el ctor.