C ++ 11 поддерживает новый синтаксис функции:
auto func_name(int x, int y) -> int;
В настоящее время эта функция будет объявлена как:
int func_name(int x, int y);
Новый стиль пока не получил широкого распространения (скажем, в gcc stl)
Однако следует ли отдавать предпочтение этому новому стилю повсюду в новых программах на C ++ 11 или он будет использоваться только при необходимости?
Лично я предпочитаю старый стиль, когда это возможно, но кодовая база со смешанными стилями выглядит довольно некрасиво.
decltype
аргументов.Ответы:
В определенных случаях необходимо использовать завершающий возвращаемый тип. В частности, лямбда-тип возврата, если он указан, должен быть указан через конечный возвращаемый тип. Кроме того, если в вашем возвращаемом типе используется тип
decltype
, требующий, чтобы имена аргументов находились в области видимости, должен использоваться конечный возвращаемый тип (однако его обычно можно использоватьdeclval<T>
для решения этой последней проблемы).У конечного возвращаемого типа есть и другие незначительные преимущества. Например, рассмотрим определение не встроенной функции-члена с использованием традиционного синтаксиса функции:
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 { // ... }
Определения типов членов не попадают в область видимости до тех пор, пока имя класса не появится раньше
::get_integers
, поэтому мы должны повторить квалификацию класса дважды. Если мы используем конечный возвращаемый тип, нам не нужно повторять имя типа:auto my_awesome_type::get_integers() const -> integer_sequence { // ... }
В этом примере это не так уж важно, но если у вас есть длинные имена классов или функции-члены шаблонов классов, которые не определены встроенными, то это может иметь большое значение в удобочитаемости.
В своем сеансе «Fresh Paint» на C ++ Now 2012 Алисдер Мередит указал, что если вы последовательно используете конечные возвращаемые типы, имена всех ваших функций будут аккуратно выстроены в линию:
auto foo() -> int; auto bar() -> really_long_typedef_name;
Я использовал конечный возвращаемые типы везде в CxxReflect , так что если вы ищете пример того , как код выглядит использовать их последовательно, вы посмотрите там (например, можете класс ).
type
источник
В дополнение к тому, что говорили другие, конечный возвращаемый тип также позволяет использовать
this
, что иначе не разрешеноstruct 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(); } };
Во втором объявлении мы использовали традиционный стиль. Однако, поскольку
this
это не разрешено в этой позиции, компилятор не использует ее неявно. Таким образом, онa.end()
использует статически объявленный тип,a
чтобы определить, какая егоend
перегрузкаvector<int>
будет вызывать, что в конечном итоге является неконстантной версией.источник
Другое преимущество заключается в том, что синтаксис конечного возвращаемого типа может быть более читабельным, когда функция возвращает указатель на функцию. Например, сравните
void (*get_func_on(int i))(int);
с участием
auto get_func_on(int i) -> void (*)(int);
Однако можно утверждать, что лучшей читаемости можно добиться, просто введя псевдоним типа для указателя функции:
using FuncPtr = void (*)(int); FuncPtr get_func_on(int i);
источник
См. Эту красивую статью: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Очень хороший пример использования этого синтаксиса без decltype в игре. :
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; }
И блестящее объяснение также украдено из статьи Алекса Аллена: «Поскольку возвращаемое значение идет в конце функции, а не перед ней, вам не нужно добавлять область видимости класса».
Сравните с этим возможным случаем, когда кто-то случайно забывает об области действия класса, и, для большей катастрофы, другой PersonType определяется в глобальной области:
typedef float PersonType; // just for even more trouble /*missing: Person::*/ PersonType Person::getPersonType () { return _person_type; }
источник
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
Первая ошибка компилятора, по крайней мере, для меня, хуже для понимания.