Что означает указатель на константную функцию?

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

Мои коллеги и я обсуждали использование «const» с указателями, и возник вопрос об использовании const с указателями на функции.

Вот несколько вопросов:

  1. В чем смысл указателя на константную функцию по сравнению с указателем на непостоянную функцию?
  2. Может ли функция быть константой?
  3. Может ли функция быть неконстантной (мутабельной)?
  4. Каков правильный (безопасный) синтаксис для передачи указателя на функцию?

Редактировать 1: синтаксис указателя функции

typedef void (*Function_Pointer)(void); // Pointer to void function returning void.

void function_a(Function_Pointer p_func); // Example 1.
void function_b(const Function_Pointer p_func); // Example 2.
void function_c(Function_Pointer const p_func); // Example 3.
void function_d(const Function_Pointer const p_func); // Example 4.

Приведенные выше объявления являются примерами обращения с указателем на функцию как с указателем на встроенный тип.

Данные, переменная или указатель памяти допускают указанные выше комбинации.
Итак, вопросы таковы: могут ли указатели на функции иметь такие же комбинации и что подразумевается под указателем на константную функцию (например, в примере 2)?


person Thomas Matthews    schedule 13.04.2012    source источник


Ответы (6)


В C нет такой вещи, как функция, являющаяся const или иным образом, поэтому указатель на константную функцию не имеет смысла (не должен компилироваться, хотя я не проверял ни с каким конкретным компилятором).

Обратите внимание, что хотя это и отличается, у вас может быть указатель const на функцию, указатель на функцию, возвращающую const, и т. д. По сути, все, кроме самой функции, может быть константой. Рассмотрим несколько примеров:

// normal pointer to function
int (*func)(int);

// pointer to const function -- not allowed
int (const *func)(int);

// const pointer to function. Allowed, must be initialized.          
int (*const func)(int) = some_func;

// Bonus: pointer to function returning pointer to const
void const *(*func)(int);

// triple bonus: const pointer to function returning pointer to const.
void const *(*const func)(int) = func.

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

person Jerry Coffin    schedule 13.04.2012
comment
Почему int (const *func)(int) нельзя? В спецификации языка указано это? - person Thomas Matthews; 13.04.2012
comment
@ThomasMatthews: TBH, я не полностью уверен, что это действительно запрещено, но если это разрешено, оно определенно игнорируется (§6.7.3/3: свойства, связанные с квалифицированными типами, имеют смысл только для выражений, которые являются lvalues.) - person Jerry Coffin; 14.04.2012
comment
Похоже, что функция const может быть полезна для объявления функции, которая при одних и тех же входных данных всегда возвращает один и тот же результат. компилятор может использовать, например, для оптимизации нескольких вызовов функции с одинаковыми входными константами в один. - person Michael; 08.10.2015
comment
@Michael: Стандартный комитет, несомненно, мог определить const, применяемый к функции, как чистую функцию (и да, это может быть полезно). До сих пор, однако, я не верю, что они сделали это, или даже уделили много внимания возможности. - person Jerry Coffin; 09.10.2015
comment
Как насчет void const * const (*const func)(int) = func: указатель const на функцию, возвращающую указатель const на const - person Michael Dorst; 25.04.2020
comment
На самом деле я не уверен, что вы можете вернуть void const * const - если вы можете, я не вижу, для чего этот последний const был бы полезен. - person Michael Dorst; 25.04.2020

Согласно спецификации C (C99, раздел 6.7.3):

Свойства, связанные с квалифицированными типами, имеют смысл только для выражений, которые являются lvalue.

Когда в спецификации говорится «квалифицированные типы», это означает вещи, определенные с помощью ключевых слов const, restrict или volatile. Функции Snice не являются lvalue, ключевое слово const в функции не имеет смысла. Возможно, вы смотрите на какое-то расширение, специфичное для компилятора. Некоторые компиляторы выдают ошибку, если вы попытаетесь объявить функцию как const.

Вы уверены, что ищете указатель на константную функцию, а не константный указатель на функцию (то есть const это указатель, а не функция)?

Относительно #4: см. это руководство, где содержится полезный обзор создания, передачи и использования функций. указатели.

person bta    schedule 13.04.2012
comment
Я смотрю на указатель на константную функцию, похожую на указатель на константные данные. - person Thomas Matthews; 13.04.2012

В C нет такой вещи, как функция const. const — это квалификатор типа, поэтому его можно использовать только для определения типа, а не функции. Может быть, вы имеете в виду константный указатель на функцию или неконстантный указатель на функцию?

В C++ методы могут быть const. Если метод const, это означает, что после вызова этого метода объект, содержащий метод, будет в том же состоянии, что и до вызова метода (ни одна из переменных экземпляра[1] не была изменена). Таким образом, вы можете указать на константный метод и неконстантный метод, и эти методы различны.

Вы можете принять указатель функции в списке аргументов как retType (*variableName)(arguments).

[1] Если только они не mutable.

person greg    schedule 13.04.2012
comment
Мы не говорим о C++ здесь. - person sidyll; 13.04.2012
comment
На самом деле, в C функции считаются постоянными, поскольку они не могут быть изменены. Я имел в виду указатель на константную функцию, следуя синтаксису, аналогичному указателю на константные данные. - person Thomas Matthews; 13.04.2012

В C функции могут быть const, если вы работаете в мире GCC! Функции могут быть объявлены const с помощью атрибутов, прикрепленных к объявлениям функций и других символов. В основном он используется для предоставления компилятору информации о том, что делает функция, даже если ее тело недоступно, поэтому компилятор может выполнить с ним какую-то оптимизацию.

Постоянная функция обычно определяется в терминах функции pure.

Чистая функция — это функция, практически не имеющая побочных эффектов. Это означает, что чистые функции возвращают значение, вычисляемое на основе заданных параметров и глобальной памяти, но не могут влиять на значение какой-либо другой глобальной переменной. Чистые функции не могут по разумным причинам не иметь возвращаемого типа (т. е. иметь возвращаемый тип void).

И теперь мы можем определить, что такое константная функция,

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

Здесь const ничего не говорит об изменчивости функций. Но это функция, которая не затрагивает глобальную память. Таким функциям можно назначать обычные указатели. В любом случае область кода, как правило, (забыв на время о самоизменяющемся коде) будет RO, и вы не сможете изменить ее с помощью обычного указателя.

Прочтите полную содержательную статью здесь.

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

person Pavan Manjunath    schedule 13.04.2012

1. В чем смысл указателя на константную функцию по сравнению с указателем на непостоянную функцию?

Нет никакой разницы между константой и неконстантой: сама функция не модифицируема.

Примечание. В C++, если функция является функцией-членом класса, const означает, что состояние объекта внутри этой функции не может быть изменено (назначаются переменные-члены, вызываются неконстантные функции-члены). В этом случае ключевое слово const является частью сигнатуры функции-члена и поэтому имеет значение с точки зрения указателя.

2.Может ли функция быть константой?

См. выше.

3.Может ли функция быть непостоянной (мутабельной)?

См. выше

4.Каков правильный (безопасный) синтаксис для передачи указателя на функцию?

Все указатели на свободные функции могут быть приведены к любому другому указателю на свободную функцию (т. е. их размер одинаков). Таким образом, вы можете определить тип для (гипотетической) функции: void f(); и преобразовать все указатели функций в этот тип для сохранения. Обратите внимание, что вы не должны вызывать функцию через этот общий тип: вам нужно привести ее обратно к исходному типу указателя на функцию, иначе вы получите неопределенное поведение (и, скорее всего, сбой)

Для C++: не гарантируется, что указатели на функции-члены могут быть преобразованы в указатели на свободные функции.

person Attila    schedule 13.04.2012

1. Синтаксически негде разместить 'const', чтобы сделать содержимое функции постоянным.

Вы столкнетесь с ошибкой «функция не является l-значением» независимо от того, есть ли у вас константа или нет.

typedef void (* FUNC)(void);
FUNC pFunc;
pFunc = 0;     // OK
*pFunc = 0;    // error:  Cannot assign to a function (a function is not an l-value)

typedef void (* const FUNC)(void);
FUNC pFunc;
pFunc = 0;     // error  (const)
*pFunc = 0;    // error:  Cannot assign to a function (a function is not an l-value)

typedef void (const * FUNC)(void);   // error:  <cv-qualifier>  (lol?)

2 и 3. Указатели на функции - да.. Содержимое функции, не похоже.

4. Я не думаю, что есть способ сделать передачу указателя на функцию более безопасной. Со всеми константами в мире единственное, что вы можете защитить, это то, что «SetCallback» не может изменить свою собственную локальную копию параметра.

typedef void (* const FUNC)(void);
void SetCallback(const FUNC const pCallback)
{
  FUNC pTemp = pCallback;   // OK  (even though pTemp is not const!!)
  pCallback = 0;            // error  (const)
}
person druid    schedule 13.04.2012