6

この質問は、次の質問に対するフォローアップの質問として意図されています: C++ におけるポインター変数と参照変数の違いは何ですか?

回答とstackoverflowで見つけたいくつかのさらなる議論を読んだことで、コンパイラは参照渡しをポインタ渡しと同じように扱うべきであり、参照は構文糖に過ぎないことがわかりました。バイナリ互換性を考慮して違いがあるかどうか、まだ把握できていないことが 1 つあります。

私たちの (マルチプラットフォーム) フレームワークでは、リリース ビルドとデバッグ ビルドの間 (およびフレームワークの異なるリリース間) でバイナリ互換性を保つ必要があります。特に、デバッグ モードでビルドするバイナリは、リリース ビルドで使用できる必要があり、その逆も同様です。これを実現するために、インターフェイスでは純粋な抽象クラスと POD のみを使用します。

次のコードを検討してください。

class IMediaSerializable
{
public:
    virtual tResult Serialize(int flags,
                              ISerializer* pSerializer,
                              IException** __exception_ptr) = 0;
//[…]
};

ISerializerIExceptionまた、純粋な抽象クラスでもあります。ISerializer既存のオブジェクトを指す必要があるため、常に NULL ポインター チェックを実行する必要があります。IExceptionポインターが指すアドレスを変更する必要がある、ある種の例外処理を実装します。このため、ポインタからポインタへのポインタを使用しますが、NULL ポインタもチェックする必要があります。

コードをより明確にし、不要な実行時チェックを取り除くために、参照渡しを使用してこのコードを書き直します。

class IMediaSerializable
{
public:
    virtual tResult Serialize(int flags,
                              ISerializer& pSerializer,
                              IException*& __exception_ptr) = 0;
//[…]
};

これで問題なく動作するようです。しかし、これが依然としてバイナリ互換性の要件を満たしているかどうかという疑問が残ります。

更新: 物事を明確にするために:この質問は、コードのポインター渡しバージョンと参照渡しバージョンの間のバイナリ互換性に関するものではありません。これがバイナリ互換でないことはわかっています。実際、バイナリの互換性を気にせずに、ポインター渡しではなく参照渡しを使用することを検討している API を再設計する機会があります (新しいメジャー リリース)。問題は、コードの参照渡しバージョンのみを使用する場合のバイナリ互換性についてです。

4

3 に答える 3

2

いいえ、使用しているコンパイラに関係なく機能しません。

2 つの関数をエクスポートするクラス Foo を考えてみましょう。

class Foo
{
public:
     void f(int*);
     void f(int&);
};

コンパイラはf、リンカーが 2 つの関数を区別できるように、2 つの関数の名前を ABI 固有の文字列に変換 (マングル) する必要があります。

現在、コンパイラはオーバーロードの解決をサポートする必要があるため、参照がポインターとまったく同じように実装されていたとしても、2 つの関数名には異なるマングル名を付ける必要があります。

たとえば、GCC はこれらの名前を次のようにマングルします。

void Foo::f(int*) => _ZN3Foo1fEPi
void Foo::f(int&) => _ZN3Foo1fERi

注意PR

そのため、関数の署名を変更すると、アプリケーションはリンクに失敗します。

于 2015-02-12T14:04:41.267 に答える