Должен ли стиль синтаксиса конечного типа возврата стать стандартным для новых программ C++11? [закрытый]


в C++11 поддерживает новый синтаксис функции:

auto func_name(int x, int y) -> int;

в настоящее время эта функция будет объявлена как:

int func_name(int x, int y);

новый стиль, похоже, еще не получил широкого распространения (скажем, в GCC stl)

однако, должен ли этот новый стиль быть предпочтительным везде в новых C++11-программах, или он будет использоваться только при необходимости?

лично я предпочитаю старый стиль, когда это возможно, но кодовая база со смешанными стилями выглядит довольно уродливо.

4 61

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;
}