Un pointeur de fonction avec un argument const peut-il être utilisé en tant que pointeur de fonction avec un argument nonconst?

Peut-être que le titre n’est pas clair en lui-même … J’ai une fonction f (fournie par une bibliothèque) qui prend comme argument un pointeur de fonction de signature void g(int*) , c’est-à-dire

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

Cependant, je voudrais l’utiliser en utilisant une fonction g (que j’ai définie) avec la signature void g(const int*) . A priori, je ne vois pas en quoi cela peut violer une constante const-correct, car toute la signature de f dit que g ne sera jamais appelé qu’avec un (non const ) int* (non const ), et en fait je peut appeler une fonction void (const int*) avec un argument non const int* .

Mais GCC se plaint et dit:

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

Je ne vois pas comment cette plainte peut être légitime. Quelqu’un sait-il si je comprends mal ou s’il existe un moyen de contourner ce problème?

Vous semblez avoir trouvé quelque chose que les rédacteurs du compilateur et des rédacteurs de normes n’ont pas expliqué. À partir du projet de loi C99, n ° 1256, paragraphe 6.7.5.3, paragraphe 15,

les parameters correspondants doivent avoir des types compatibles.

Notez que const int * n’est pas compatible avec int * . Cependant, int * peut être converti en const int * . À partir du § 6.3.2.3, paragraphe 2,

Pour tout qualificatif q , un pointeur sur un type non qualifié peut être converti en un pointeur sur la version qualifiée de q

Des règles plus sophistiquées permettant de déduire quand il est acceptable de substituer des types dérivés de versions qualifiées ou non qualifiées du même type ne sont tout simplement pas présentes dans la norme. Par conséquent, votre code est techniquement en violation de la norme.

Ma conclusion: il me semble que le compilateur devrait traiter cette erreur comme une erreur “: votre code n’est pas techniquement conforme à la norme, mais sa signification est claire et le code absolument sûr. N’hésitez pas à écrire une demande de fonctionnalité à votre fournisseur de compilateur. Il y a beaucoup de pratiques non conformes qui ne génèrent pas d’avertissements sans – -pedantic .

Pour finir, j’ai compilé avec Clang et le compilateur m’a informé que l’avertissement était pédant. Cependant, je n’avais pas demandé d’avertissements pédants … il semble donc n’y avoir aucun moyen de l’éteindre.

 warning: types de pointeurs incompatibles passant 'void (int const *)', attendu 'void (*) (int *)'
       [-pédant]

Solution: utilisez une dissortingbution explicite.

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

Vous avez raison, il n’y a aucune raison pour que C devrait refuser cet appel (autre que parce que la norme C dit qu’il le devrait). U(*)(T*) devrait être un sous-type de U(*)(const T*) car int* est un sous-type de const int* par substituabilité.

Pourquoi C ne permet pas cela, je ne sais pas.

En ce qui concerne les solutions de contournement, vous pouvez fournir une fonction de proxy:

 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) 

Le fait d’utiliser un proxy sûr, conforme aux normes, comme celui-ci devrait suffire à prouver que l’appel aurait dû être valide.