Тип класса не определен в статическом члене лямбда-функции в C ++ 17 - PullRequest
2 голосов
/ 18 октября 2019

Некоторое время назад я пытался реализовать свои собственные виртуальные таблицы (в образовательных целях). Для этого я использовал указатели на функции и лямбды. При этом я наткнулся на проблему. Вместо публикации всего кода V-таблицы я просто написал Minimal Reproducible Example.

По какой-то причине он не компилируется (C2027 - использование неопределенного типа Test):

class Test
{
    int n;
    static inline auto f = [](Test* t)
    {
        return t->n;
    };
};

Даже когда я объявляю Test следующим образом: class Test;.

Мне любопытно, что вызывает такое поведение, потому что и 1010 *, и class Test объявлены до лямбды.

Я использую последнюю версию Visual Studio 2019 с C ++ 17.

Ответы [ 2 ]

2 голосов
/ 18 октября 2019

Вы не можете сделать это, потому что класс еще не является полным типом в точке лямбда-выражения.

[class.mem]

6 Полный контекст класса представляет собой

  • тело функции ([dcl.fct.def.general]),

  • аргумент по умолчанию,

  • noexcept-спецификатор, или

  • инициализатор элемента по умолчанию

в пределахспецификация члена класса. [Примечание: контекст полного класса вложенного класса также является контекстом полного класса любого включающего класса, если вложенный класс определен в спецификации члена включающего класса. - примечание конца]

7 Класс считается полностью определенным типом объекта ([basic.types]) (или завершенным типом) при закрытии} спецификатора класса. Класс считается завершенным в контексте полного класса;в противном случае он считается неполным в своей спецификации члена класса.

2 голосов
/ 18 октября 2019

Test является объявленным до лямбды, но оно не определено . Определение класса заканчивается только в конце самого класса, когда вы делаете };. До этого это неполный тип.

И вы не можете получить доступ к членам неполного типа. Или, по крайней мере, не через экземпляр объекта, как вы делаете в своей лямбде (вы можете говорить о Test::n в этот момент, но вы не можете взять Test* и сделать ->n на нем).

Теперь вы можете сказать, что если вы сделаете f нормальным статическим членом, вы можете легко поместить определение класса туда, и оно будет работать. Это связано с тем, что в C ++ есть специальное правило для определения функций-членов класса. А именно, тела этих функций считаются определенными, как если бы они были размещены сразу после определения класса. Таким образом, они могут внутренне использовать класс, как если бы он был полным типом, поскольку тела этих функций будут определены в месте, где они являются законченным типом.

Ваша лямбда не является функцией-членом;это лямбда-функция, назначаемая статической переменной класса. Инициализаторы статических переменных не получают такой специальной обработки, поэтому Test считается неполным внутри тела лямбда-функции.

...