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

92

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

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

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

int func_name(int x, int y);

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

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

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

мирк
источник
29
Это в основном для decltypeаргументов.
Cat Plus Plus
что говорит CatPlusPlus: не имеет особого смысла использовать это в вашем примере
stijn
@Cat Plus Plus Это означает, что вы оставляете все как есть в C ++ 03, если вам не нужно определять тип возвращаемого значения?
mirk
1
Ужасно указывать "авто" перед каждой функцией. Это похоже на пикантный ответ С ++ на "def" Python?
Эрик Аронести,

Ответы:

110

В определенных случаях необходимо использовать завершающий возвращаемый тип. В частности, лямбда-тип возврата, если он указан, должен быть указан через конечный возвращаемый тип. Кроме того, если в вашем возвращаемом типе используется тип 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

Джеймс МакНеллис
источник
1
Пока не похоже, что есть консенсус, но интересно посмотреть на CxxReflect в новом стиле.
mirk
Привет, Джеймс. Этот ответ, вероятно, можно было бы сделать более точным в свете стандарта C ++ 14.
Дрю Дорманн
@DrewDormann Что бы вы добавили / изменили?
underscore_d
Выравнивание на самом деле является большим плюсом, до такой степени, что мне хотелось бы, чтобы здесь было новое ключевое слово func, которое заменило бы бессмысленное слово auto.
Johan
67

В дополнение к тому, что говорили другие, конечный возвращаемый тип также позволяет использовать 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>будет вызывать, что в конечном итоге является неконстантной версией.

Йоханнес Шауб - litb
источник
2
Хотя это хорошая / изящная демонстрация концепции (с использованием членов в возвращаемых типах), это забавно, поскольку в C ++ 14 указание типа полностью избыточно во встроенном определении без преобразования; теперь мы можем просто использовать вывод полного возвращаемого типа. : P
underscore_d
27

Другое преимущество заключается в том, что синтаксис конечного возвращаемого типа может быть более читабельным, когда функция возвращает указатель на функцию. Например, сравните

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);
s3rvac
источник
10

См. Эту красивую статью: 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;
}
Петр Нич
источник
7
Я не уверен, что это относится к категории "катастроф": если тип неправильный, код не будет компилироваться. Ошибки времени выполнения могут иметь катастрофические последствия; ошибок времени компиляции, не так много.
Джеймс МакНеллис,
4
@JamesMcNellis сравнивает вывод компилятора: 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 Первая ошибка компилятора, по крайней мере, для меня, хуже для понимания.
PiotrNycz
Лично я не согласен, мне труднее читать второе сообщение, и я бы предпочел, чтобы реализация выглядела как декларация.
младший