Должен ли стиль синтаксиса конечного типа возврата стать стандартным для новых программ C++11? [закрытый]
в C++11 поддерживает новый синтаксис функции:
auto func_name(int x, int y) -> int;
в настоящее время эта функция будет объявлена как:
int func_name(int x, int y);
новый стиль, похоже, еще не получил широкого распространения (скажем, в GCC stl)
однако, должен ли этот новый стиль быть предпочтительным везде в новых C++11-программах, или он будет использоваться только при необходимости?
лично я предпочитаю старый стиль, когда это возможно, но кодовая база со смешанными стилями выглядит довольно уродливо.
4 ответа:
есть определенные случаи, когда вы должны использовать конечный тип возврата. В частности, лямбда-тип возврата, если он указан, должен быть указан через конечный тип возврата. Кроме того, если ваш тип возврата использует
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 { // ... }
элементы typedefs не находятся в области видимости до тех пор, пока имя класса не появится перед
::get_integers
, поэтому мы должны повторить квалификацию класса дважды. Если мы используем конечный тип возврата, нам не нужно повторять имя типа:auto my_awesome_type::get_integers() const -> integer_sequence { // ... }
в этом примере это не так уж и важно, но если у вас есть длинные имена классов или функции-члены шаблонов классов, которые не определены встроенный, то он может сделать большую разницу в удобочитаемости.
в своем "Свежая Краска" session на C++Now 2012, Alisdair Meredith отметил, что если вы используете конечные возвращаемые типы последовательно, имена всех ваших функций выстраиваются аккуратно:
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>
он будет звонить, что в конечном итоге является неконстантной версией.
еще одно преимущество заключается в том, что синтаксис типа trailing-return может быть более читаемым, когда функция возвращает указатель на функцию. Например, сравните
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; }