Почему закрывающий тип статического лямбда-элемента неполный?

Сегодня я попытался сделать что-то подобное. Я был удивлен, что он не скомпилировался.

struct Test {
//  v----- Remove me to compile
    //  /*
    static constexpr auto get_test1 = [](Test const& self) {
        return self.test; // error, Test is incomplete
    };
    // */

    // Handwritten version of the lambda
    struct {
        constexpr auto operator() (Test const& self) const {
            return self.test; // ok
        }
    }
    static constexpr get_test2{};

    int test;
};

Живой пример

В нем говорится, что тип Test является неполным в объеме. Однако рукописная версия лямбды действительно работает. Какова техническая причина этого? Является ли это надзором в стандарте или существует конкретная формулировка, которая делает Test неполным в лямбда?

Ответы

Ответ 1

Это то, что я мог найти:

§5.1.2 Лямбда-выражения [expr.prim.lambda]

  1. [...] [Примечание: Имена, указанные в lambda-declarator, просматриваются в контексте, в котором появляется лямбда-выражение. -End note]

  2. Компонент-оператор лямбда-выражений дает тело функции (8.4) оператора вызова функции, но для целей поиска по имени (3.4), [...] составной оператор рассматривается в контексте лямбда-выражения,

Если я не неправильно понимаю стандарт, это означает, что Test и self как в параметре, так и в теле рассматриваются/рассматриваются в контексте лямбда, который является областью класса, в которой Test является неполным.

Что касается того, почему это разрешено с вложенным классом:

§9.2 Члены класса [class.mem]

  1. Класс считается полностью определенным типом объекта (3.9) (или полным типом) при закрытии} класса. В классе класса-члена класс считается полным в телах функций, аргументах по умолчанию, использования-деклараций, представляющих наследующие конструкторы (12.9), спецификаций-спецификаций s и скрепок-или-равных-инициализаторов s для нестатических элементов данных (включая такие вещи во вложенных классах). В противном случае он считается неполным в своей спецификации класса.

Ответ 2

Я думаю, что ваш компилятор прав. Test по-прежнему не завершен. Обработка такого инициализатора элементов данных (который требует завершения типа) не откладывается до конца определения класса.

Я не мог найти часть Стандарта, которая занимается этой проблемой, но я помню, что она была поднята как комментарий Национального органа проекта С++ 17 (US 24, P0488R0):

Текущая спецификация запрещает constexpr static элементы данных того же типа, что и охватывающий класс.

Пример:

struct A {
   int val;
   static constexpr A cst = { 42 };  // error
}; 

Очевидно, что снятие полного ограничения может привести к некоторым нарушениям, чтобы комментарий "не увеличивал консенсус".