Подтвердить что ты не робот

Лямбда-захват объекта constexpr

GCC 4.7.2 компилирует это:

constexpr int i = 5;
[]{ std::integral_constant< int, i >(); }; // nonstandard: i not captured

но не это:

constexpr int i = 5;
[&i]{ std::integral_constant< int, i >(); }; // GCC says i not constexpr

Последний пример кажется мне правильным, согласно С++ 11 §5.1.2/15:

Объект захватывается ссылкой, если он неявно или явно захвачен, но не захвачен копией. Не указано, объявлены ли в типе закрытия объявленные дополнительные элементы нестатического элемента для объектов, захваченных ссылкой.

Кажется, что захваченный объект i внутри лямбды относится к переменной в охватывающей области, которая constexpr, а не просто ссылка const.

В стандарте явно указано, что использование захвата по-значению преобразуется в использование соответствующего элемента лямбда-объекта. И я думаю, что 5.1.2 намекает, что моя интерпретация верна.

Есть ли что-либо, прямо указывающее, относится ли захват по ссылке к объекту в охватывающей области или ссылке?

4b9b3361

Ответ 1

Второй аргумент шаблона std::integral_constant< int, i > относится к шаблону-параметру непиковой формы, в частности, к интегральному или перечисляемому типу (14.3.2p1 bullet 1) и поэтому должен быть преобразованным константным выражением типа int.

В лямбда-выражении неявный захват происходит, когда сущность не используется в составном заявлении (5.1.2p11); использование преобразованного константного выражения в явном экземпляре экземпляра не является odr-use (3.2p3), поэтому справедлив первый пример.

Во втором примере я думаю, что gcc неверно отклонять его; 5.1.2p17 говорит в примечании, что:

Идентификатор, не являющийся неприемлемым, относится к исходному объекту, а не к члену типа закрытия.

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

Ответ 2

Во-первых, я могу подтвердить ваше наблюдение с помощью gcc 4.6.3 и clang 3.0 на Ubuntu 12.04.

У меня нет стандарта С++ 11 (только черновик), поэтому я не могу прокомментировать это. Но посмотрите, по моему мнению, эквивалентные утверждения

constexpr int i = 5;
const int &j = i;
std::integral_constant<int, j>();

Ни gcc, ни clang не компилирует это, потому что j не является "интегральной константой".