Perhaps the title isn't clear in itself...
I have a function f (provided by some library) that takes as an argument a function pointer of signature void g(int*)
, i.e.
void f(void (*g)(int*));
However, I would like to use it using a function g
(that I defined) with signature void g(const int*)
. A priori, I can't see how this can violate any const-correctness, as all the signature of f
says is that g
will only ever be called with a (non-const
) int*
(non-const
), and indeed I can call a void (const int*)
function with a non-const
int*
argument.
But GCC complains and says,
expected 'void (*)(int *)', but argument is of type 'void (*)(const int *)'
I can't see how this complaint can be legitimate, so does anyone know whether my understanding of that is wrong, or if there is a way around that?
You seem to have found something that the compiler writers and standards writers did not account for. From C99 draft n1256, §6.7.5.3 paragraph 15,
corresponding parameters shall have compatible types.
Note that const int *
is not compatible with int *
. However, int *
may be converted to const int *
. From §6.3.2.3, paragraph 2,
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type
More sophisticated rules for inferring when it is acceptable to substitute types derived from qualified or unqualified versions of the same type are simply not present in the standard. Therefore, your code is technically in violation of the standard.
My conclusion: It seems to me that this error should be treated as "pedantic" by the compiler: your code does not technically conform to the standard, but the meaning is unambiguous and the code is absolutely safe. Feel free to write a feature request to your compiler vendor. There are plenty of nonconformant practices which do not generate warnings without -pedantic
.
As a final note, I compiled with Clang and the compiler informed me that the warning was pedantic. However, I had not requested pedantic warnings... so there appears to be no way to turn it off.
warning: incompatible pointer types passing 'void (int const *)', expected 'void (*)(int *)' [-pedantic]
Workaround: Use an explicit cast.
void g(const int *);
f((void (*)(int *)) g);
You are right, there's no reason C should disallow that call (other than because the C standard says it should). U(*)(T*)
should be a sub-type of U(*)(const T*)
because int*
is a sub-type of const int*
through substitutability.
Why C does not allow this, I don't know.
As for work-arounds, you can provide a proxy function:
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)
The fact that using a safe, standard-compliant proxy like this works at all should be proof enough that the call should have been valid in the first place.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With