Можно ли использовать указатель на функцию с аргументом const в качестве указателя на функцию с неконстантным аргументом? - PullRequest
16 голосов
/ 23 ноября 2011

Возможно, заголовок сам по себе непонятен ... У меня есть функция f (предоставленная некоторой библиотекой), которая принимает в качестве аргумента указатель на функцию подписи void g(int*), то есть

void f(void (*g)(int*));

Тем не менее, я хотел бы использовать его с помощью функции g (которую я определил) с подписью void g(const int*).Априори, я не могу понять, как это может нарушить какую-либо константность, поскольку все подписи f говорят о том, что g будет вызываться только с (не- const) int* (не- const), и действительно, я могу вызвать функцию void (const int*) с аргументом, отличным от const int*.

Но GCC жалуется и говорит:

expected 'void (*)(int *)', but argument is of type 'void (*)(const int *)'

Iне могу понять, как эта жалоба может быть законной, поэтому кто-нибудь знает, неверно ли мое понимание этого, или есть ли способ обойти это?

Ответы [ 2 ]

6 голосов
/ 23 ноября 2011

Похоже, вы нашли что-то, что авторы компиляторов и разработчики стандартов не учли. Из проекта C99 N1256, § 6.7.5.3, пункт 15,

соответствующие параметры должны иметь совместимые типы.

Обратите внимание, что const int * не совместимо с int *. Однако int * может быть преобразовано в const int *. Из §6.3.2.3, пункт 2,

Для любого квалификатора q указатель на неквалифицированный тип q может быть преобразован в указатель на q квалифицированную версию тип

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

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

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

warning: incompatible pointer types passing 'void (int const *)', expected 'void (*)(int *)'
      <b>[-pedantic]</b>

Обходной путь: Использовать явное приведение.

void g(const int *);

f((void (*)(int *)) g);
3 голосов
/ 23 ноября 2011

Вы правы, нет никакой причины, по которой C должен запретить этот вызов (кроме как из-за того, что стандарт C говорит, что должен).U(*)(T*) должен быть подтипом U(*)(const T*), потому что int* является подтипом const int* через заменяемость.

Почему C не допускает этого, я не знаю.

Что касается обходных путей, вы можете предоставить прокси-функцию:

void foo(const int* x) { ... } // <-- the function you want to pass in

void bar(int* x) { foo(x); } // proxy

f(bar); // instead of f(foo)

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

...