16

として宣言された関数を含む C ライブラリがある場合、void g(void (*callback)());次のコードは洗練されていますが、違法です。

struct A
{
    // error C2159: more than one storage class specified (VC++ Nov 2012 CTP)
    static extern "C" void callback()
    {}
};

g(A::callback); 

C++11 がこれをサポートしていないのはなぜですか?

4

2 に答える 2

19

これは、入り込むのに特に紛らわしいトピックです。§7.5「リンケージ仕様」[dcl.link]を攻撃しましょう。

1) すべての関数型、外部リンケージを持つ関数名、および外部リンケージを持つ変数名には言語リンケージがあります。

言語リンケージのプロパティは、タイプ名前という2 つのまったく異なる種類のエンティティに適用されることに注意してください。

関数は、その型に、どの ABI に準拠しているかを識別する、通常は見えない情報を持っています。目に見えない言語タグ。

別の言語の変数または関数の名前は、C++ を介して構文的にアクセスすることも、C++ 宣言を参照する他の言語からアクセスすることもできます。しかし、すべての言語が C++ の命名方式と OO モデルに対応できるわけではありません。したがって、このスキームのインターフェースにはクラスが含まれません。

これらは別々に管理されるため、型 (呼び出し規約) と名前 (リンカー シンボル) が異なるリンケージを持つものを持つことができます。

4) リンケージ仕様のネスト。リンケージ仕様がネストすると、最も内側の仕様が言語リンケージを決定します。リンケージ仕様は有効範囲を確立しません。リンケージ仕様は、名前空間スコープ (3.3) でのみ発生するものとします。リンケージ仕様では、指定された言語リンケージは、すべての関数宣言子の関数型、外部リンケージを持つ関数名、およびリンケージ仕様内で宣言された外部リンケージを持つ変数名に適用されます。クラス メンバの名前とクラス メンバ関数の関数型の言語リンケージを決定する際に、AC 言語リンケージは無視されます。

extern "C" {}、メンバー関数を除く、ポインターと参照を含むすべての関数宣言に影響します。関数は名前空間またはメンバーとしてのみ定義できるため、C 関数は名前空間スコープでのみ定義できます。

標準では、次の例が示されています。

extern "C" typedef void FUNC_c();

class C {
   // the name of the function mf1 and the member 
   // function’s type have C++ language linkage; the 
   // parameter has type pointer to C function
   void mf1(FUNC_c*);

   // the name of the function mf2 and the member
   // function’s type have C++ language linkage
   FUNC_c mf2;

   // the name of the data member q has C++ language
   // linkage and the data member’s type is pointer to
   // C function
   static FUNC_c* q;
};

ただし、を使用して必要な動作をエミュレートできtypedefます。§7.5/4 の別の例から、

extern "C" typedef void FUNC();

// the name f2 has C++ language linkage and the 
// function’s type has C language linkage
FUNC f2;

これらの例をあなたのものと組み合わせると、次のことができます

extern "C" typedef void callback_t();

callback_t A_callback; // declare function with C++ name and C type

struct A
{
    static callback_t &callback; // not a member function
};

// in source file:

// definition matches semantics of declaration, although not syntax
void A_callback() { ... }

// define static member reference
callback_t &A::callback = A_callback;

g(A::callback); // call syntax is emulated

実際には、ほとんど違いはありません。C と C++ は、ほとんどのプラットフォームで互換性のある呼び出し規則を使用します (例外については、このページの Jonathan Wakely のコメントを参照してください)。これは、C++ のあまり実装されていない機能です。これは、用語の混乱を招くオーバーロードと、微妙なものから学術的なものまでの概念的な違いのためです。

于 2013-01-18T09:46:55.987 に答える
3

まず、関数宣言正当です。extern "C"ただし、 はクラス メンバーに対しては無視されるため、 が を期待している場合はgextern "C" void (*)()を渡すことはできませんcallback

この理由については、もともとは主に直交性の問題だったと思います。クラスメンバー関数がextern "C"一般的であることは意味がなく、直交性(または単に静的メンバーのケースを誰も考慮していない)は、これは静的メンバー関数にも当てはまりますが、それらを許可するとextern "C"便利でした。現在 (つまり C++11)、ルールを変更すると既存のコードが壊れる可能性があるため、問題が発生する可能性があります。私見ですが、変更によって壊れるコードの量はおそらく非常に小さいため、変更は受け入れられます。また、破損は実行時のセマンティクスの変更ではなく、コンパイル時エラーを引き起こすため、簡単に検出して修正できます。それでも、私の知る限り、誰もこれを変更する提案をしなかったため、変更されませんでした。

于 2013-01-18T08:54:42.800 に答える