C++では、関数型には「言語リンケージ」が関連付けられています。これは「C++」、「C」、またはその他の実装定義の言語のいずれかです。標準では、言語リンケージが異なる2つの関数型は、たとえそれ以外は同一であっても、明確に異なる型であると明記されています。この考え方は、一部の実装ではC++関数とC関数で呼び出し規約が異なる可能性があるため、それらを混在させることができないというものです
しかし、gccとclangは、言語リンケージ情報を型に保存していません。そのため、言語リンケージが異なる2つの関数は同一になることがあります:
#include <type_traits>
extern "C" using c_func = void ();
// this static assertion should fail, but it doesn't
static_assert(std::is_same<c_func, void ()>::value);
これは、関数ポインタパラメータを取る関数をオーバーロードする際に、誤ったコンパイルエラーを引き起こすこともあります:
extern "C" using c_func = void ();
void f(c_func *) {}
void f(void (*)()) {}
上記のコードはコンパイルされるべきですが、gccとclangの両方とも、パラメータリストが同一であると判断し、One Definition Ruleに違反するとみなします
この問題の責任はgccやclangにあるのではなく、標準にあると思います。つまり、標準が間違っており、実装定義とするよう更新されるべきです。gccとclangはこの動作を変更できません。なぜなら、それはABIの破壊的な変更になるからです(マングル名内のextern "C"関数型は異なる方法でエンコードされることになります)。また、この変更を行う理由もありません。CとC++の呼び出し規約は、私の知る限り、ほぼすべてのプラットフォームで同一だからです
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.