Подтвердить что ты не робот

Почему родовые лямбды разрешены, а вложенные структуры с шаблонами не являются?

Насколько я понимаю - общие лямбды преобразуются в объекты локальных структур структуры с шаблоном operator(). Это делает общий лямбда очень мощным и простым в использовании инструментом. С другой стороны, можно создать структуры, вложенные в функцию, но, тем не менее, структура имеет шаблонный элемент, например:

#include <iostream>

int main() {
    struct inner {
    template <class T>
       void operator()(T &&i) { }
    };
    return 0;
}

или сам шаблон:

int main() {
    template <class T>
    struct inner {
       void operator()(T &&i) { }
    };
    return 0;
}
Компилятор

похоже, имеет проблему с его компиляцией:

error: invalid declaration of member template in local class

и

error: a template declaration cannot appear at block scope

Я предполагаю, что проблема лежит в стандарте С++, чем в ошибке компилятора. Каковы причины, по которым лямбдам разрешено иметь шаблонные члены, а не локальные структуры?

Я нашел этот qustion, но я думаю, что ответ отчасти устарел (я не думаю, что это правда даже для С++ 11).

4b9b3361

Ответ 1

Это основная проблема 728, которая была подана до того, как родовые лямбды были чем-то.

Вы упомянули общие лямбды и что они были идентичны локальным классам с соответствующим членом template operator(). Однако на самом деле их нет, а различия связаны с характеристиками реализации. Рассмотрим

template <typename T>
class X {
    template <typename>
    void foo() {
        T t;
    }
};

и

template <typename T>
auto bar() {
    return [] (auto) {T t;};
};

Выполнение этих шаблонов с помощью <void> будет в порядке, но плохо сформировано во втором. Почему хорошо в первом случае? foo не обязательно должен быть инстанциативным для каждого конкретного T, но только один из них (это будет [temp.res]/(8.1)).

Почему плохо сформировалось во втором случае? Генерируемое тело лямбды создается - частично - используя предоставленные аргументы шаблона. И причиной этого частичного экземпляра является тот факт, что...

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

(Richard Smith) Мы должны создать достаточный экземпляр локального "шаблона", чтобы сделать его независимым от локального контекста (который включает параметры шаблона шаблона закрывающей функции).

Это также связано с обоснованием [expr.prim.lambda]/13, в котором указывается, что объект неявно захватывается лямбдой, если он...

обозначает объект в потенциально-оцененном выражении ([basic.def.odr]), где охватывающее полное выражение зависит от общего параметра лямбда, объявленного в пределах области охвата лямбда-выражения.

То есть, если у меня есть лямбда, например [=] (auto x) {return (typename decltype(x)::type)a;}, где a - это какая-то переменная блока-объекта из закрывающей функции, независимо от того, является ли x членом typedef для void или нет, приведение будет вызвать захват a, потому что мы должны решить это, не дожидаясь вызова lambda. Для обсуждения этой проблемы см. Оригинальное предложение для генерических лямбда.

Суть в том, что полностью откладывание экземпляра шаблона-члена несовместимо с моделью, используемой (по крайней мере одной) основной реализацией (-ами), и поскольку это ожидаемая семантика, эта функция не была введена.


Была ли это оригинальная мотивация для этого ограничения? Он был введен где-то в период с января по май 1994 года, без каких-либо документов, охватывающих его, поэтому мы можем получить только приблизительное представление о преобладающих представлениях этой статьи обоснование того, почему локальные классы не должны быть аргументами шаблона:

Шаблоны классов и классы, созданные из шаблона, являются глобальной областью и не могут ссылаться на локальные объекты сущности.

Возможно, тогда кто-то хотел KISS.

Ответ 2

Я предполагаю, что проблема лежит в стандарте С++

Правильно. Это предусмотрено в [temp] для шаблонов классов:

Объявление шаблона может отображаться только как область пространства имен или объявление класса.

и [temp.mem] для шаблонов участников:

Локальный класс типа non-clos не должен иметь шаблонов-членов.


Каковы причины, по которым лямбдам разрешено иметь шаблонные члены, а не локальные структуры?

Поскольку, как только у нас были lambdas на С++ 11, было сочтено, что было бы чрезвычайно полезно расширить эту концепцию, чтобы иметь общие лямбды. Для такого расширения языка было предложение, которое было пересмотрено и пересмотрено и принято.

С другой стороны, пока еще не было представлено предложение (насколько мне известно из краткого поиска), в котором излагаются мотивация необходимости создания шаблонов членов в локальных классах, которые недостаточно решены общий лямбда.

Если вы считаете, что это важная проблема, которая должна быть решена, не стесняйтесь отправить предложение после того, как выложите продуманную мотивацию для почему важны шаблоны локальных членов.