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

Функция возврата функции С++

Где в стандарте есть функции, возвращающие функции, запрещенные? Я понимаю, что они концептуально смешны, но мне кажется, что грамматика позволит им. Согласно этой веб-странице, noptr-declarator [является] допустимым декларатором", который будет включать в себя декларатор функции:

int f()();

Относительно синтаксиса.

Мне кажется, что синтаксис, описанный в [dcl.decl], позволяет

int f(char)(double)

который можно интерпретировать как функцию f, которая принимает char и возвращает функцию с той же сигнатурой, что и int g(double).

1    declarator:
2       ptr-declarator
3       noptr-declarator parameters-and-qualifiers trailing-return-type
4    ptr-declarator:
5        noptr-declarator
6        ptr-operator ptr-declarator
7    noptr-declarator:
8        declarator-id attribute-specifier-seq opt
9        noptr-declarator parameters-and-qualifiers
10       noptr-declarator [ constant-expression opt ] attribute-specifier-seq opt
11       ( ptr-declarator )
12    parameters-and-qualifiers:
13       ( parameter-declaration-clause ) cv-qualifier-seqAfter

Грубо говоря, после 1- > 2, 2 = 4, 4- > 6, 4- > 6 Вы должны иметь   ptr-оператор ptr-оператор ptr-operator Затем для первого декларатора используйте 4- > 5, 5 = 7, 7- > 8; используйте 4- > 5, 5 = 7, 7- > 9 для второго и третьего деклараторов.

4b9b3361

Ответ 1

Из [dcl.fct] довольно явно:

Функции не должны иметь тип возвращаемого типа массива или функции, хотя они могут иметь тип возврата указатель типа или ссылку на такие вещи. Массивов функций не должно быть, хотя могут быть массивы указателей на функции.

С С++ 11 вы, вероятно, просто хотите:

std::function<int()> f();
std::function<int(double)> f(char);

Существует некоторая путаница в отношении грамматики С++. Утверждение int f(char)(double); может быть проанализировано в соответствии с грамматикой. Вот дерево разбора:

grammar

Кроме того, такой синтаксический анализ имеет смысл даже на основе [dcl.fct]/1:

В объявлении T D, где D имеет вид     D1 (параметр-объявление-предложение) cv-qualifier-seq opt
        ref-qualifier opt исключение-спецификация opt атрибут-спецификатор-seq opt
и тип содержащегося идентификатора declarator в объявлении T D1 является "производным-декларатором-типом-списком T", тип идентификатора-декларатора в D - это функция-производный-декларатор-тип-список (предложение-объявление-предложение) cv-qualifier-seq optref-qualifier opt возвращает T ".

В этом примере T == int, D == f(char)(double), D1 == f(char). Тип идентификатора-декларатора в T D1 (int f(char)) является "функцией (char), возвращающей int". Таким образом, list-declarator-type-list является "функцией возврата (char)". Таким образом, тип f будет считан как функция функции (char), возвращающая функцию (double), возвращающую int. "

В конце концов, это очень много шума, поскольку это явно запрещенная форма декларатора. Но не по грамматике.

Ответ 2

С С++ 11 (но не предыдущие версии С++) вы можете не только возвращать C-подобные указатели на функции, но и С++ закрывает, особенно с анонимные функции. См. Также std:: function

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

Кстати, я не думаю, что вы могли бы вернуть целые функции. Что это значит? Как бы вы это реализовали? Практически говоря, функция представляет собой некоторый блок кода, а его имя (например, для массивов) является указателем на начало машинного кода функции.

Хорошим трюком может быть создание (использование механизмов за пределами стандарта С++) функции во время выполнения (а затем обработка указателя на ее функцию). Некоторые внешние библиотеки могут разрешить это: вы можете использовать библиотеку JIT (например, asmjit, gccjit, LLVM...) или просто сгенерировать код на С++, затем скомпилировать и dlopen и dlsym в системах POSIX и т.д..

PS. Вероятно, вы правильно понимаете, что грамматика С++ 11 (правила EBNF в стандарте) не запрещает возвращаемые функции. Это семантическое правило, сформулированное на простом английском языке, которое запрещает это (это не какое-либо правило грамматики). Я имею в виду, что только EBNF позволит:

 // semantically wrong... but perhaps not syntactically
 typedef int sigfun_T(std::string);
 sigfun_T foobar(int);

и именно для semantics причины (не из-за правил EBNF), что компилятор справедливо отвергает вышеуказанный код. Практически говоря, таблица символов имеет большое значение для компилятора С++ (и это не синтаксическая или контекстно-свободная грамматика).

Печальный факт о С++ заключается в том, что (по старым причинам) его грамматика (одна) очень неоднозначна. Следовательно, С++ 11 трудно читать (для людей), трудно писать (для разработчиков), трудно разобрать (для компиляторов),....

Ответ 3

Формальная грамматика языка C фактически запрещает возвращаемые функции, но всегда можно вернуть указатель на функцию, которая для всех целей и целей выглядит так, как вы хотите:

  int (*getFunc())(int, int) { … }

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

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

Ответ 4

Если вы не возвращаете указатель или ссылку на функцию, которая в порядке, альтернатива возвращает копию функции. Теперь подумайте о том, как выглядит копия функции, действует, как, ведет себя как. Это, прежде всего, будет массив байтов, который также не разрешен, и, во-вторых, все эти байты будут эквивалентностью части кода, буквально возвращающей кусок кода... почти все эвристические антивирусные сканеры считают, что вирус, поскольку также не было бы возможности проверить жизнеспособность кода, возвращаемого системой времени выполнения или даже во время компиляции. Даже если вы можете вернуть массив, как бы вы вернули функцию? Основная проблема с возвратом массива (который будет копией в стеке) заключается в том, что размер неизвестен, и поэтому нет способа удалить его из стека, и существует такая же дилемма для функций (где массив будет двоичный код машинного языка). Кроме того, если вы вернули функцию таким образом, как бы вы развернулись и вызвали эту функцию?

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

Надеюсь, это имеет смысл

Ответ 5

На самом деле в C нельзя передать или вернуть функцию. Может быть передан/возвращен только указатель/адрес функции, который концептуально близок. Честно говоря, благодаря возможности ommiting & и * с указателями функций не должно волновать, передана функция или указатель (если она не содержит статических данных). Вот простое объявление указателя функции:

void (*foo)();

foo - это указатель на функцию, возвращающую void и не принимающую аргументов.

В С++ это не так уж и много. Можно по-прежнему использовать указатели функций C-стиля или новый полезный std::function объект для всех вызываемых произведений. С++ также добавляет лямбда-выражения, которые являются встроенными функциями, которые как-то похожи на замыкания на функциональных языках. Это позволяет вам не делать все переданные функции глобальными.

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