todas - Omitir tipo de retorno en C++ 11
tipos de funciones en lenguaje c (7)
¿Tu tipo de retorno realmente cambia eso a menudo? ¿Por qué no puedes simplemente especificarlo explícitamente? Ejemplo:
template<class T>
int f(T&& x)
{
...
}
La gente hizo eso por más de veinte años ...
Recientemente me encontré usando la siguiente macro con gcc 4.5 en modo C ++ 11:
#define RETURN(x) -> decltype(x) { return x; }
Y escribiendo funciones como esta:
template <class T>
auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))
He estado haciendo esto para evitar el inconveniente de tener que escribir efectivamente el cuerpo de la función dos veces, y haber mantenido los cambios en el cuerpo y el tipo de retorno sincronizados (lo que en mi opinión es un desastre que está por suceder).
El problema es que esta técnica solo funciona en una línea de funciones. Entonces cuando tengo algo como esto (ejemplo complicado):
template <class T>
auto f(T&& x) -> ...
{
auto y1 = f(x);
auto y2 = h(y1, g1(x));
auto y3 = h(y1, g2(x));
if (y1) { ++y3; }
return h2(y2, y3);
}
Entonces tengo que poner algo horrible en el tipo de retorno.
Además, cada vez que actualice la función, tendré que cambiar el tipo de retorno, y si no lo hago correctamente, obtendré un error de compilación si tengo suerte, o un error de ejecución en el peor de los casos. Tener que copiar y pegar cambios en dos ubicaciones y mantenerlos sincronizados, creo que no es una buena práctica.
Y no puedo pensar en una situación en la que quisiera un lanzamiento implícito en lugar de un lanzamiento explícito.
Seguramente hay una manera de pedirle al compilador que deduzca esta información. ¿De qué sirve que el compilador lo mantenga en secreto? Pensé que C ++ 11 fue diseñado para que no se requiera tal duplicación.
EDIT: oops, me acabo de dar cuenta de que hay una diferencia de alcance entre el especificador de tipo trailing-return y la declaración de retorno. Específicamente:
auto f(int a)
{
char r[sizeof(f(a))+1];
return r;
}
Kaboom!
Respuesta anterior:
Es desafortunado que el lenguaje no proporcione una sintaxis para que el compilador infiera el tipo de retorno en este caso, porque es trivial mostrar que la inferencia es posible.
Específicamente, estamos hablando del caso donde hay exactamente una declaración de retorno dentro de la función.
Independientemente de en qué parte de la función se encuentra esa declaración de devolución, o de la complejidad del código anterior, debe quedar claro que la siguiente transformación es posible:
return (ugly expression);
dentro
auto return_value = (ugly expression);
return return_value;
Si el compilador puede inferir el tipo de valor de return_value
(y de acuerdo con las reglas C ++ 0x, puede), entonces el tipo inferido de valor de return_value
se puede elegir como el tipo de retorno de la función.
Por lo tanto, me parece que una modificación a C ++ 0x donde el especificador de tipo de retorno final solo debería ser necesaria cuando la multiplicidad de declaraciones de retorno no es exactamente una posible y resolvería el problema.
Estoy de acuerdo con Yttrill. La deducción del tipo de devolución ya ha demostrado ser una práctica factible en idiomas como Haskell, y como C ++ ya ha logrado el "auto", es solo un paso más para lograr la deducción del tipo de devolución. Esta deducción debe ocurrir en el momento de la especialización, no de la definición de la plantilla, ya que se necesita información del tipo real suministrada a la plantilla. La declaración y definición separadas ya no es una práctica común en C ++ genérico, porque el cuerpo de la plantilla debe escribirse en archivos de encabezado y, por lo tanto, los cuerpos de la plantilla casi siempre van acompañados de declaraciones de plantilla. En situaciones en las que hay múltiples declaraciones de devolución y los tipos de ellas no coinciden, el compilador puede informar felizmente sobre el error. En resumen, la deducción por tipo de devolución es totalmente posible en C ++, si el comité lo desea. Y es MUY importante, ya que la duplicación de tipos de retorno de escritura manual dificulta el uso generalizado de pequeñas funciones de ayuda genéricas, que es una práctica común en la programación funcional y genérica.
La justificación de este comportamiento se da en el borrador, 8.3.5p12:
Un tipo-retorno-retorno es más útil para un tipo que sería más complicado de especificar antes del declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
más bien que
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
Por lo tanto, esto solo está destinado a simplificar el caso en el que la referencia a los nombres de parámetros ayuda.
Si asume que C ++ siempre podría inferir el tipo de retorno de las funciones del cuerpo de la función: esto no va a volar. C ++ (y C) tiene como objetivo permitir la modularidad al separar la declaración de la implementación, por lo que en el punto de la llamada, es posible que no tenga el cuerpo de la función disponible. Sin embargo, cada persona que llama necesita conocer los tipos de parámetros y el tipo de retorno de cada función / método al que se llama.
Muchos lenguajes de programación, incluidos Ocaml y Felix, pueden deducir el tipo de retorno de una función y no requieren que se especifique. En Ocaml puedes y debes especificarlo en una interfaz. En Felix he encontrado que para algunas funciones es conveniente especificarlo en el código de la biblioteca para que sea más fácil de usar.
Estoy sorprendido de que "auto" no funcione para los tipos de devolución, ciertamente estaba en el plato. ¿Fue esto considerado demasiado difícil de implementar? [No es trivial dado que una función puede ser recursiva].
Ohhh ... ahora lo veo. El problema es la característica de diseño de plantillas estúpidas "nombres dependientes":
template<class T> auto f(T a) { return a.f(); }
Entonces, si bien es un poco complicado calcular el tipo de retorno de una función ordinaria debido a la sobrecarga, es imposible con las plantillas debido a la búsqueda de nombres dependientes: solo se puede hacer después de la creación de instancias. Pero la sobrecarga debe suceder antes de eso. Por lo tanto, los tipos de devolución automática no pueden ir en el idioma porque no se generalizan a las plantillas.
Parece que g ++ 4.8 está obteniendo una implementación de deducción de tipo de retorno automático. El parche fue puesto por Jason Merrill, quien también está enviando un documento para C ++ - 1Y para la función. La característica está disponible con -std = c ++ 1y.
Sigo jugando con eso.
Si simplemente está intentando establecer el tipo de retorno, conviértalo en un argumento de plantilla. De esta manera puede cambiar todo lo relacionado con el tipo de retorno sin cambiar realmente la función. Puedes poner un tipo de retorno predeterminado si quieres "Me gusta" en este ejemplo.
template <class R = int, class T>
R f(T&& x)
{
...
return h2(y2, y3);
}
El siguiente código demuestra su efectividad.
CÓDIGO DEMO:
#include <iostream>
#include <iomanip>
template <class T, class S>
T h2(T& x, S& y)
{
return x + y;
}
template <class R = int, class T>
R f(T& x)
{
auto y2 = x;
auto y3 = x;
return h2(y2, y3);
}
int main(int argc, char** argv)
{
int x = 7;
std::string str = "test! ";
auto d = f<double>(x);
auto i = f(x); // use default type (int)
auto s = f<std::string>(str);
std::cout << std::fixed << std::setprecision(4);
std::cout << "double: " << d << std::endl;
std::cout << "int: " << i << std::endl;
std::cout << "string: " << s << std::endl;
return 0;
}
SALIDA:
double: 14.0000
int: 14
string: test! test!
Desafortunadamente, la funcionalidad exacta que está buscando no existe (todavía) y no es parte de la especificación de C ++ 0x. Sin embargo, es posible que esto pueda ser parte de la especificación de C ++ 1x cuando se redacta. Hasta entonces, quédate con las plantillas.