Когда мы можем опустить возвращаемый тип в лямбда С++ 11?

Насколько я знаю, в стандартном С++ 11 (не С++ 14), когда опуская тип возврата лямбда, его тип возврата выводится следующим образом:

  • Тип возвращаемого выражения, когда лямбда состоит только из одного оператора return с выражением или
  • void во всех остальных случаях.

Рассмотрим теперь этот код:

#include <iostream>

auto closure = [](int x)
{
    x++;
    return x;
};

int main()
{
    int y = closure(10);
    std::cout << y << std::endl;
}

Это должно подпадать под случай 2., однако код компилируется, как если бы были С++ 14 с выводом типа auto, как в g++ 4.9.2, g++ 5, так и в clang++, с -pedantic -Wall -Wextra -std=c++11. Что здесь происходит? Я неправильно интерпретирую этот стандарт?

Ответ 1

Ваш код принимается без каких-либо предупреждений, потому что исходное ограничение С++ 11 считается дефектом в стандарте, что позволяет реализациям исправлять поведение. См. CWG DR975, DR1048 и N3638.

975. Ограничения на вычет типа возврата для lambdas

[Перешел к статусу DR на совещании в апреле 2013 года в рамках статьи N3638.]

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

1048. автоматический вывод и вычет типа лямбда-возврата.

...

Примечания к ноябрьскому заседанию в 2014 году:

CWG согласилась с тем, что изменение, внесенное в документ N3638, должно рассматриваться как DR против С++ 11.

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

DR1048 определяет несоответствие, когда правила вывода типа возврата для обычных функций с использованием типа заполнителя auto немного отличаются от правил, предложенных в DR975. В частности, вывод возвращаемого типа для нормальных функций будет отбрасывать cv-квалификаторы верхнего уровня во всех случаях, где, как и для лямбда-выражений, будут сохраняться cv-квалификаторы для типов классов.

N3638 разрешает эту проблему, среди прочих.


Я сомневаюсь, что есть способ вернуться к первоначальному поведению, не найдя версию компилятора, которая поставляется с поддержкой С++ 11 лямбда до реализации DR выше.

Ответ 2

Некоторые правила С++ 14 доступны в режиме С++ 11, когда авторы компилятора считали это слишком сложным для реализации обоих правил сразу.

Ответ 3

Это то, что я нашел в проекте С++ Draft N3337:

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

- если составной оператор имеет вид

{выражение-specifier-seq optreturn выражение; }

тип возвращаемого выражения после преобразования lvalue-to-rvalue (4.1), преобразование матрицы в указатель (4.2) и преобразования функции в указатель (4.3);

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

[Пример:

auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
                                  // braced-init-list is not an expression)

- конец примера]

Стандарт, похоже, указывает, что:

  • Когда присутствует только один оператор, и
  • Это оператор return и
  • Возвращаемый объект представляет собой выражение

Затем возвращаемый тип выводится из выражения. В противном случае тип возврата void.