Présentation du problème
En C++, les types de fonctions ont une « liaison de langage » associée : il s'agit soit de « C++ », soit de « C », soit d'une autre liaison de langage définie par l'implémentation. Le standard spécifie explicitement que deux types de fonctions avec des liaisons de langage différentes sont des types distincts, même s'ils sont identiques par ailleurs.
Ceci est dû au fait que certaines implémentations peuvent avoir des conventions d'appel différentes pour les fonctions C++ par rapport aux fonctions C, ce qui les rend incompatibles.
Problème avec GCC et Clang
Cependant, GCC et Clang ne stockent pas d'informations de liaison de langage avec le type. Ainsi, deux fonctions avec des liaisons de langage différentes peuvent être identiques :
extern "C" using c_func = void ();
static_assert(std::is_same<c_func, void ()>::value);
Ceci peut également causer des erreurs de compilation lors du surchargement d'une fonction qui prend un pointeur de fonction en paramètre :
extern "C" using c_func = void ();
void f(c_func *) {}
void f(void (*)()) {}
Implications et limites
Le problème ne semble pas être dû à GCC ou Clang, mais plutôt au standard lui-même. Le standard devrait être mis à jour pour rendre ceci défini par l'implémentation. GCC et Clang ne peuvent pas changer leur comportement car cela entraînerait un changement d'ABI (Application Binary Interface) cassant.
De plus, les conventions d'appel pour les fonctions C et C++ sont identiques sur la plupart des plateformes, ce qui rend ce changement inutile.
Conclusion
En résumé, le problème de non-conformité de GCC et Clang avec le standard C++ est dû à une faille dans le standard lui-même. Une mise à jour du standard pour rendre ceci défini par l'implémentation est nécessaire pour résoudre ce problème.