Лямбда-захват объекта 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 намекает, что моя интерпретация верна.
Есть ли что-либо, прямо указывающее, относится ли захват по ссылке к объекту в охватывающей области или ссылке?
Ответы
Ответ 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
не является "интегральной константой".