Как объявить lambda operator() как noreturn?
Как можно объявить operator()
лямбда как noreturn
?
Ideone принимает следующий код:
#include <cstdlib>
int main() {
[]() [[noreturn]] { std::exit(1); }();
return 0;
}
Clang 3.5 отклоняет его:
error: 'noreturn' attribute cannot be applied to types
Вы можете попробовать его в godbolt: http://goo.gl/vsuCsF
Какой из них прав?
Обновить: соответствующие стандартные разделы выглядят как 5.1.2.5, 7.6.3, 7.6.4, но после прочтения это все равно не на 100% ясен мне (i), что такое правильное поведение, (ii) как отметить оператор() лямбда как noreturn
.
Ответы
Ответ 1
Кланг верен. Атрибут может указывать на объявляемую функцию или на ее тип; они разные. [[noreturn]]
должен принадлежать самой функции. Разницу можно увидеть в
// [[noreturn]] appertains to the entity that being declared
void f [[noreturn]] (); // §8.3 [dcl.meaning]/p1:
// The optional attribute-specifier-seq following a
// declarator-id appertains to the entity that is declared."
[[noreturn]] void h (); // §7 [dcl.dcl]/p2:
// "The attribute-specifier-seq in a simple-declaration
// appertains to each of the entities declared by
// the declarators of the init-declarator-list."
// ill-formed - [[noreturn]] appertains to the type (§8.3.5 [dcl.fct]/p1:
// "The optional attribute-specifier-seq appertains to the function type.")
void g () [[noreturn]] {}
Действительно, если вы скомпилируете это в g++, он сообщает вам, что
warning: attribute ignored [-Wattributes]
void g () [[noreturn]] {}
^
note: an attribute that appertains to a type-specifier is ignored
Обратите внимание, что он не выдаёт предупреждение о том, что g()
действительно возвращается.
Поскольку "атрибут-спецификатор-seq в лямбда-деклараторе относится к типу соответствующего оператора вызова функции или шаблону оператора" (§5.1.2 [expr.prim.lambda]/p5), а не к этому оператору/сам оператор, вы не можете использовать [[noreturn]]
там. В более общем плане язык не дает вам возможности применить атрибут к operator ()
самой лямбда.
Ответ 2
Таким образом, лямбда-делькаратор имеет следующую грамматику в стандартном разделе проекта С++ 5.1.2
Лямбда-выражения:
( parameter-declaration-clause ) mutableopt exception-specificationopt attribute-specifier-seqopt trailing-return-typeopt
и атрибут noreturn действительно является допустимым атрибутом-спецификатором-seq, поэтому с точки зрения грамматики я не вижу ограничения из раздела 7.6.3
Атрибут Noreturn, который он говорит (акцент мой вперед):
[...] Атрибут может быть применен к идентификатору declarator в функции декларация. [...]
который, кажется, не запрещает ваше использование, но он предполагает, что он не разрешен. Если мы посмотрим на раздел 7.6.4
Задает атрибут зависимости, он говорит:
[...] Атрибут может быть применен к идентификатору-декларатору Объявление параметра в объявлении функции или лямбда [...]
тот факт, что он явно включает в себя ламповый случай, сильно указывает на то, что раздел 7.6.3
предназначен для исключения лямбда и, следовательно, clang
будет правильным. В качестве дополнительной заметки Visual Studio также отклоняет этот код.
Ответ 3
[C++11: 7.6.3/1]:
Атрибут-токен noreturn
указывает, что функция не возвращается. Он должен появляться не более одного раза в каждом списке атрибутов, и не должно присутствовать атрибут-аргумент-предложение. Атрибут может быть применен к идентификатору объявления в объявлении функции. Первое объявление функции должно указывать атрибут noreturn
, если любое объявление этой функции указывает атрибут noreturn
. Если функция объявлена с атрибутом noreturn
в одной единицы перевода, и одна и та же функция объявлена без атрибута noreturn
в другой единицы перевода, программа плохо сформирована; не требуется диагностика.
Я признаю, что эта формулировка, как есть, не запрещает атрибуту появляться в другом месте, но, несмотря на отсутствие доказательств в каком-либо стандарте для него, я не думаю, что это предназначено для работы с объявлениями лямбда.
Следовательно, Clang был бы правильным.
Может или не может быть сказано, что было предложение заплатки Clang, чтобы разрешить атрибуты noreturn
в стиле GCC на lambdas, но а не стандартная форма.
К сожалению, эта функция не включена в GCC список расширений, поэтому я не могу точно видеть, что происходит здесь.