Почему закрывающий тип статического лямбда-элемента неполный?
Сегодня я попытался сделать что-то подобное. Я был удивлен, что он не скомпилировался.
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]
-
[...] [Примечание: Имена, указанные в lambda-declarator, просматриваются в контексте, в котором появляется лямбда-выражение. -End note]
-
Компонент-оператор лямбда-выражений дает тело функции (8.4) оператора вызова функции, но для целей поиска по имени (3.4), [...] составной оператор рассматривается в контексте лямбда-выражения,
Если я не неправильно понимаю стандарт, это означает, что Test
и self
как в параметре, так и в теле рассматриваются/рассматриваются в контексте лямбда, который является областью класса, в которой Test
является неполным.
Что касается того, почему это разрешено с вложенным классом:
§9.2 Члены класса [class.mem]
- Класс считается полностью определенным типом объекта (3.9) (или полным типом) при закрытии} класса. В классе класса-члена класс считается полным в телах функций, аргументах по умолчанию, использования-деклараций, представляющих наследующие конструкторы (12.9), спецификаций-спецификаций s и скрепок-или-равных-инициализаторов s для нестатических элементов данных (включая такие вещи во вложенных классах). В противном случае он считается неполным в своей спецификации класса.
Ответ 2
Я думаю, что ваш компилятор прав. Test
по-прежнему не завершен. Обработка такого инициализатора элементов данных (который требует завершения типа) не откладывается до конца определения класса.
Я не мог найти часть Стандарта, которая занимается этой проблемой, но я помню, что она была поднята как комментарий Национального органа проекта С++ 17 (US 24, P0488R0):
Текущая спецификация запрещает constexpr static
элементы данных того же типа, что и охватывающий класс.
Пример:
struct A {
int val;
static constexpr A cst = { 42 }; // error
};
Очевидно, что снятие полного ограничения может привести к некоторым нарушениям, чтобы комментарий "не увеличивал консенсус".