2012-09-22 12 views
5

Estoy tratando de proporcionar una función que llame a una lambda proporcionada, y me pregunto si es posible hacer que devuelva un valor predeterminado si la función tiene el tipo de retorno void.¿Hay alguna manera de coincidir en el valor de retorno en C++ 11?

Esto es lo que tengo hasta ahora, una función que devuelve el valor de retorno de la lambda, pero si el lambda es void, devuelve 20.

#include <functional> 
#include <iostream> 

template <typename H> 
auto f(H&& h) -> decltype(h(), void()) 
{ 
    return h(); 
} 

template <typename H> 
auto f(H&& h) -> decltype(h(), int()) 
{ 
    h(); 
    return 20; 
} 

int main() 
{ 
    int r = f([](){ std::cout << "test1" << std::endl; return 10; }); // error here 
    std::cout << "r: " << r << std::endl; 
    r = f([](){ std::cout << "test2" << std::endl; }); 
    std::cout << "r: " << r << std::endl; 

    return 0; 
} 

Esto produce el error,

test.cpp:20:68: error: call of overloaded ‘f(main()::<lambda()>)’ is ambiguous 

Por lo tanto, esto se debe simplemente a que C++ no puede usar el polimorfismo en función del tipo de devolución. Sin embargo, me pregunto si hay algunos buenos trucos de C++ 11, como un mejor uso de decltype o alguna magia de plantilla, que podría hacer esto posible. Lo estoy preguntando porque, por lo que puedo ver, no hay ninguna ambigüedad real aquí ... el compilador está infiriendo el tipo de retorno void y diciendo que es ambiguo si coincide con la versión int o void de f, lo cual es un poco tonto.

Una razón para hacer esto es que si la función f espera un valor de retorno, pero el usuario proporciona una lambda que no incluya una declaración return, el compilador infiere un tipo void, y los errores hacia fuera. Dado que en el escenario real tengo una buena idea de lo que debería ser un valor de retorno predeterminado razonable cuando el usuario no se preocupa de proporcionar uno, me pregunto si es posible hacer que el compilador permita a los usuarios descuidar la declaración return que a menudo es innecesaria por conveniencia.

Gracias. Debo mencionar que estoy usando GCC 4.6.3.

respuesta: en base a sugerencia de utilizar enable_if de Xeo, se me ocurrió lo siguiente, que parece funcionar:

template <typename H> 
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), void>::value, int>::type 
{ 
    h(); 
    return 20; 
} 

template <typename H> 
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), int>::value, int>::type 
{ 
    return h(); 
} 

Gracias!

+0

has probado f en las funciones normales? – CharlesB

+0

Sí, en las funciones normales también es ambiguo. – Steve

+0

Segundo parámetro de 'decltype': ¿Puedo preguntar para qué sirve? (Realmente, porque no lo sé!). – 0x499602D2

Respuesta

3

Puede usar std :: enable_if.

Su tipo de devolución sería std::enable_if<!std::is_same<decltype(h()), void>::value, decltype(h())>:type para la primera y std::enable_if<std::is_same<decltype(h()), void>::value, int>::type para la segunda. Eso debería hacer que tu código funcione. No lo he probado, así que no estoy seguro si funciona completamente.

+0

Me temo que todavía devuelve el error "ambiguo". – Steve

+0

@Steve: No, esto funciona correctamente. Creo que podrías tener un error tipográfico en alguna parte. Ver [esto] (http://liveworkspace.org/code/e0c94a11d120300743e202f4daec8a66). – Xeo

+0

@Xeo: Ah, gracias, creo que el segundo 'decltype (h())' en la primera parte de la solución de JKor fue problemático, ya que cambió el valor de retorno para 'f()'. Lo hice funcionar basado en tus ejemplos, publicaré el código en la pregunta. – Steve

2

Dame una razón sobre por qué el compilador debería ser capaz de inferir por qué sobrecarga seleccionar según lo que haces dentro de la función? En C++, la resolución de sobrecarga solo se refiere si todos los argumentos son compatibles con un parámetro. El tipo de devolución y el contenido de la función no son de interés lo que alguna vez.

As STL says:

TLDR: Las plantillas son codiciosos! No los sobrecargues a menos que estés absolutamente seguro de lo que sucederá.

Y una referencia universal (template<class T> void f(T&&);) como parámetro es lo más avaricioso que puede obtener.

se puede resolver como @JKor dice con SFINAE, pero simplificada:

#include <type_traits> 

template<class F> 
auto f(F f) 
    -> decltype(true? f() : void()) 
{ 
    f(); 
} 

template <class F> 
auto f(F f) -> decltype(int(f())) 
{ 
    f(); 
    return 42; 
} 

int main(){ 
    f([]{}); 
} 

Tenga en cuenta que GCC 4.7 tiene un error donde decltype(int(void_returning_function())) no causa la función de SFINAE a cabo. 3.1 sonido metálico hace correctamente

f([]{}); 

trabajo, aswell como

f([]{ return 42; }); 
Cuestiones relacionadas