Когда мы можем опустить возвращаемый тип в лямбда С++ 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
.