Непонятный синтаксис

241
06 марта 2017, 12:42

Зачем нужен данный синтаксис, ведь тип результата не вычисляется автоматом?

auto foo(int arg) -> int {}

Answer 1

Этот синтаксис появился в результате включения в стандарт C++ лямбда-выражений.

Лямбда-выражение может быть записано, например, как,

auto foo = [](int arg) -> int { /*...*/ }; 

Это лямбда-выражение может быть преобразовано в функцию, имеющую тип int( int )

Этот синтаксис переняли для объявления функций. У функции в ее начале должен присутствовать спецификатор(ы) типа возвращаемого выражения. Так как реальный тип возвращаемого значения указывается после списка параметров, то в качестве спецификатора возвращаемого значения в объявлении функции используется спецификатор auto.

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

Поэтому этот синтаксис удобен, например, при объявлении шаблонных функций.

Рассмотрите следующую демонстрационную программу.

#include <iostream>
template <class T, class U>
auto foo(const T &x, const U &y) -> decltype( x + y )
{
    return x + y;
}
int main()
{
        int x = 10;
        int y = 20;
        std::cout << typeid(foo(x, y)).name() << std::endl;
        long z = 30;
        std::cout << typeid(foo(x, z)).name() << std::endl;
        float f = 40;
        std::cout << typeid(foo(x, f)).name() << std::endl;
}

Вывод программы на консоль, например, в MS VC++ может выглядеть как

int
long
float

Заранее сказать, какой будет тип возвращаемого выражения, невозможно. Он зависит от типов параметров функции и от типа вычисляемого выражения. Использование спецификатора типа auto в данном примере облегчает объявление функции.

Answer 2

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

template<class T>
???  f(const T& t)
{
    return t*5.0;
}

Что тут поставить вместо ???? А вдруг T - это класс с переопределенным оператором умножения? который возвращает объект другого класса?

А вот так

template<class T>
auto  f(const T& t) -> decltype(t*5.0)
{
    return t*5.0;
}

никаких проблем...

Answer 3
  • Он нужен, в частности, для того, чтобы поместить тип возврата в тот контекст, где видны имена параметров функции, соответственно, известна их семантика. Например

    auto foo(short a, short b) -> decltype(a + b) { ... }
    

    В данном случае компилятор учтет тот факт, что операнды выражения a + b будут подвергнуты integral promotions и [на большинстве платформ] дадут результат типа int. В то же время на платформах, где integral promotions для short дают unsigned int тип возврата функции автоматически станет unsigned int. (Желательна такая "гибкость" или нет - зависит от контекста.)

  • Также при определении метода класса за пределами определения класса такой тип считается находящимся в области видимости класса, что влияет на процесс поиска неквалифицированных имен

    typedef void *Ret;
    struct A {
      typedef int Ret;
      Ret foo();
      Ret bar();
    };
    auto A::foo() -> Ret { return 0; } // OK, `Ret` обозначает именно `A::Ret`
    Ret A::bar() { return 0; } // Oшибка - `Ret` обозначает `::Ret`
    

    При использовании "классического" синтаксиса в определении пришлось бы указывать квалифицированное имя типа A::Ret.

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

  • При использовании этого синтаксиса вы получаете возможность пользоваться "компактной" записью составных типов (аналогично тому, как это делается в using вместо typedef)

    auto foo() -> int (*)[20] { ... }
    

    В "классической" записи это выглядело бы как

    int (*foo())[20] { ... }
    

    что многие сочтут менее удобочитаемым вариантом.

READ ALSO
Парсинг принтеров

Парсинг принтеров

Помогите распарсить эту сроку: интересуют такие параметры на название принтера и дата установки

255
Не срабатывает фунция execve() в Linux

Не срабатывает фунция execve() в Linux

При компиляции через g++ 54

275
Запись в бинарный файл

Запись в бинарный файл

Здравствуйте, помогите пожалуйста, решить проблему

254
Рефакторинг большой функции

Рефакторинг большой функции

Дана функция на 200 строк, обрабатывающая большое количество данных, принимающая много параметровПутём неимоверных усилий функция была порезана...

234