C ++ 11 admite una nueva sintaxis de función:
auto func_name(int x, int y) -> int;
Actualmente esta función se declararía como:
int func_name(int x, int y);
El nuevo estilo no parece haber sido ampliamente adoptado todavía (digamos en el gcc stl)
Sin embargo, ¿debería preferirse este nuevo estilo en todas partes en los nuevos programas de C ++ 11, o sólo se utilizará cuando sea necesario?
Personalmente, prefiero el estilo antiguo cuando es posible, pero una base de código con estilos mixtos se ve bastante fea.
c++
c++11
auto
trailing-return-type
tenebroso
fuente
fuente
decltype
los argumentos.Respuestas:
Hay ciertos casos en los que debe utilizar un tipo de retorno final. En particular, un tipo de retorno lambda, si se especifica, debe especificarse mediante un tipo de retorno final. Además, si su tipo de retorno utiliza un
decltype
que requiere que los nombres de los argumentos estén dentro del alcance, se debe usar un tipo de retorno final (sin embargo, generalmente se puede usardeclval<T>
para solucionar este último problema).El tipo de retorno final tiene otras ventajas menores. Por ejemplo, considere una definición de función miembro no en línea utilizando la sintaxis de función tradicional:
struct my_awesome_type { typedef std::vector<int> integer_sequence; integer_sequence get_integers() const; }; my_awesome_type::integer_sequence my_awesome_type::get_integers() const { // ... }
Las definiciones de tipo de miembro no están dentro del alcance hasta que el nombre de la clase aparece antes
::get_integers
, por lo que tenemos que repetir la calificación de la clase dos veces. Si usamos un tipo de retorno final, no necesitamos repetir el nombre del tipo:auto my_awesome_type::get_integers() const -> integer_sequence { // ... }
En este ejemplo, no es tan importante, pero si tiene nombres de clase largos o funciones miembro de plantillas de clase que no están definidas en línea, entonces puede marcar una gran diferencia en la legibilidad.
En su sesión "Fresh Paint" en C ++ Now 2012, Alisdair Meredith señaló que si usa tipos de retorno finales de manera consistente, los nombres de todas sus funciones se alinean ordenadamente:
auto foo() -> int; auto bar() -> really_long_typedef_name;
He usado tipos de retorno finales en todas partes en CxxReflect , por lo que si está buscando un ejemplo de cómo se ve el código usándolos de manera consistente, puede echar un vistazo allí (por ejemplo, la
type
clase ).fuente
Además de lo que otros dijeron, el tipo de retorno final también permite usar
this
, que de otra manera no está permitidostruct A { std::vector<int> a; // OK, works as expected auto begin() const -> decltype(a.begin()) { return a.begin(); } // FAIL, does not work: "decltype(a.end())" will be "iterator", but // the return statement returns "const_iterator" decltype(a.end()) end() const { return a.end(); } };
En la segunda declaración, usamos el estilo tradicional. Sin embargo, dado
this
que no está permitido en esa posición, el compilador no lo usa implícitamente. Entonces,a.end()
usa el tipo declarado estáticamente dea
para determinar quéend
sobrecargavector<int>
va a llamar, que termina siendo la versión no constante.fuente
Otra ventaja es que la sintaxis del tipo de retorno al final puede ser más legible cuando la función devuelve un puntero a una función. Por ejemplo, compare
void (*get_func_on(int i))(int);
con
auto get_func_on(int i) -> void (*)(int);
Sin embargo, se puede argumentar que se puede lograr una mejor legibilidad simplemente introduciendo un alias de tipo para el puntero de función:
using FuncPtr = void (*)(int); FuncPtr get_func_on(int i);
fuente
Vea este buen artículo: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Muy buen ejemplo de cuándo usar esta sintaxis sin decltype en el juego :
class Person { public: enum PersonType { ADULT, CHILD, SENIOR }; void setPersonType (PersonType person_type); PersonType getPersonType (); private: PersonType _person_type; }; auto Person::getPersonType () -> PersonType { return _person_type; }
Y una explicación brillante también robada del artículo de Alex Allain "Debido a que el valor de retorno va al final de la función, en lugar de antes, no es necesario agregar el alcance de la clase".
Compare con este posible caso cuando uno por accidente se olvida del alcance de la clase y, para un desastre mayor, se define otro PersonType en el alcance global:
typedef float PersonType; // just for even more trouble /*missing: Person::*/ PersonType Person::getPersonType () { return _person_type; }
fuente
prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'
vs.prog.cpp:13:1: error: 'PersonType' does not name a type
El primer error del compilador es, al menos para mí, peor de entender.