Не ошибается ли моя книжная дискуссия о типах возврата лямбда?

В моей книге сказано:

Lambdas с телами функций, которые содержат что-либо, кроме одного оператора return, который не указывает возвращаемый тип return return.

но это:

auto f = []{
  int i=0; i++;
  return std::string("foo");
};
std::cout << f() << std::endl;

фактически компилирует и выводит "foo", но этот lambda expr имеет больше, чем просто один оператор return, поэтому он должен возвращать void, потому что он не указывает вручную "- > std::string" в качестве возвращаемого типа.

Что здесь происходит?

Я использую компилятор Apple в последнем Xcode 4.6, основанный на Clang 3.2, кажется:

clang --version

Apple LLVM версия 4.2 (clang-425.0.24) (на основе LLVM 3.2svn) Цель: x86_64-apple-darwin12.2.0 Модель темы: posix

Ответ 1

Книга точно отражает правила в проекте n3290 Стандарта. Возможно, ваш компилятор реализовал другой проект.

В разделе 5.1.2p4 проект гласит

Если лямбда-выражение не включает тип возвращаемого типа возврата, это как будто тип trailing-return-type обозначает следующий тип:

  • если составной оператор имеет вид    { выражение-specifier-seq optreturn выражение ; }тип возвращаемого выражения после преобразования lvalue-to-rval, преобразования матрицы в указатель и преобразования функции в указатель;
  • в противном случае void.

Синтаксическая конструкция attribute-specifier-seq может быть alignas или атрибутами с двойной привязкой. Непеременные объявления.

Проект n3485, который после публикации С++ 11 (т.е. работает в направлении С++ 1y), содержит ту же формулировку. Я не знаю, было ли другое правило в каком-то проекте раньше, чем n3290.

Ответ 2

Если вы используете популярные компиляторы (gcc, Visual Studio), вам обычно не нужно указывать тип возвращаемого значения, если компилятор может определить его однозначно - как в вашем примере.

В следующем примере показана lambda, для которой требуется явная информация о возвращаемом типе:

auto lambda = [](bool b) -> float
    { 
        if (b) 
            return 5.0f; 
        else 
            return 6.0; 
    };

Я спросил Бьярна Страуструпа по этому поводу, его комментарий:

I do not know if C++11 allows the deduction of the return type is there are several return statements with identical return type. If not, that's planned for C++14.

Ответ 3

Я не уверен, что делать из цитаты в вопросе, но вот что говорит о стандарте С++ 11 о lambdas без декларатора или типа возвращаемого значения:

Если лямбда-выражение не содержит лямбда-декларатора, оно равно если лямбда-декларатор был(). Если лямбда-выражение не включают тип trailing-return-type, это похоже на тип возвращаемого типа возврата обозначает следующий тип (5.1.2p4):

- если составной оператор имеет вид {expression-specifier-seqopt return expression; } тип возвращаемое выражение после преобразования lvalue-to-rvalue (4.1), преобразование массива в указатель (4.2) и преобразование функции в указатель (4.3);

- в противном случае void.

Ответ 4

Clang реализует предлагаемое решение С++ core issue 975. Это позволяет произвольному телу для лямбда с любым числом операторов return и выводит возвращаемое значение из возвращаемого выражения при условии, что все они должны производить один и тот же тип.

В С++ 14 эта поддержка далее обобщается N3638, которая была проголосована за рабочий проект стандарта на Бристольское совещание РГ21.

Ответ 5

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