Доступ к объектам с обобщенным захватом в типе возвратного возврата лямбда

int main()
{
    auto l = [x = 10]() -> decltype(x) {};
}   
  • clang++ 4.0 отклоняет этот код со следующей ошибкой:

    error: use of undeclared identifier 'x'
            auto l = [x = 10]() -> decltype(x) {};
                                            ^
    
  • g++ 7 отклоняет этот код со следующей ошибкой:

    In function 'int main()':
    error: 'x' was not declared in this scope
      auto l = [x = 10]() -> decltype(x) {};
                                      ^
    error: 'x' was not declared in this scope
    In lambda function:
    warning: no return statement in function returning non-void [-Wreturn-type]
      auto l = [x = 10]() -> decltype(x) {};
                                      ^
    

Является ли это ошибкой или есть что-то в стандарте, которое явно предотвращает использование объектов, захваченных с использованием обобщенного синтаксиса С++ 14, в типе возвращаемого возврата лямбда?


Обратите внимание, что оба компилятора довольны не обобщенными захватами:

int main()
{
    int x = 10;
    auto l = [x]() -> decltype(x) { return 0; };
}   

Ответ 1

TL; DR. Компиляторы ведут себя так, как ожидалось.

Стандарт определяет лямбда-семантику следующим образом: [expr.prim.lambda, section 1]:

лямбда-выражение:

   lambda-introducer lambda-declarator_opt compound-statement

Здесь составной оператор - это всего лишь тело лямбда между {}, потому что все остальное включено в lambda-declarator:

лямбда-описатель:

   ( parameter-declaration-clause ) decl-specifier-seq_opt
       exception-specification_opt attribute-specifier-seq_opt trailing-return-type_opt

Кроме того, в разделе 12 той же главы сказано, что

Выполнение init-capture ведет себя так, как будто оно объявляет и явно фиксирует переменную формы "auto init-capture"; чья декларативная область является составным выражением лямбда-выражений, за исключением того, что:

(12.1) - если захват копируется (см. ниже), нестатический член данных, объявленный для захвата, и переменная рассматривается как два разных способа обращения к одному и тому же объекту, который имеет время жизни нестатический элемент данных, и никакая дополнительная копия и уничтожение не выполняется, а

(12.2) - если захват по ссылке, время жизни переменных заканчивается, когда заканчивается время жизни объектов закрытия.

Итак, в вашем первом примере область переменной x - это только тело лямбда, не включая выражение decltype. Во втором примере, очевидно, область x - это функция main.