1. ホーム
  2. c++

[解決済み] operator&がオーバーロードされたときに、オブジェクトのアドレスを確実に取得するにはどうしたらよいですか?

2022-04-22 16:40:17

質問

次のようなプログラムを考えてみましょう。

struct ghost
{
    // ghosts like to pretend that they don't exist
    ghost* operator&() const volatile { return 0; }
};

int main()
{
    ghost clyde;
    ghost* clydes_address = &clyde; // darn; that's not clyde's address :'( 
}

どうすれば clyde のアドレスは?

あらゆる種類のオブジェクトに対して等しく機能するソリューションを探しています。 C++03のソリューションがあればいいのですが、C++11のソリューションにも興味があります。 可能であれば、実装に依存した動作は避けましょう。

私が認識しているのは、C++11の std::addressof 関数テンプレートですが、ここで使用することには興味がありません。 標準ライブラリの実装者がこの関数テンプレートをどのように実装するかを理解したいのです。

どのように解決するのですか?

更新してください。 C++11では std::addressof の代わりに boost::addressof .


まず、Boostからコンパイラの回避ビットを除いたコードをコピーしてみましょう。

template<class T>
struct addr_impl_ref
{
  T & v_;

  inline addr_impl_ref( T & v ): v_( v ) {}
  inline operator T& () const { return v_; }

private:
  addr_impl_ref & operator=(const addr_impl_ref &);
};

template<class T>
struct addressof_impl
{
  static inline T * f( T & v, long ) {
    return reinterpret_cast<T*>(
        &const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
  }

  static inline T * f( T * v, int ) { return v; }
};

template<class T>
T * addressof( T & v ) {
  return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}

を渡すとどうなるのでしょうか? への参照は、関数 ?

addressof へのポインタと一緒に使用することはできません。

C++では、もし void func(); が宣言されている場合 func は引数を取らず、結果を返さない関数への参照です。この関数への参照は,関数へのポインタに些細な変換が可能である -- からである. @Konstantin : 13.3.3.2では T &T * は関数の区別がつかない。1つ目はID変換、2つ目は関数からポインタへの変換で、どちらも"完全一致のランクを持っています(13.3.3.1.1 table 9)。

その 関数への参照 を経由して addr_impl_ref を選択した場合、オーバーロードの解決に曖昧さがあります。 f というダミー引数によって解決されます。 0 であり、これは int に昇格する可能性があります。 long (インテグラルコンバージョン)。

このように、単純にポインタを返すだけです。

変換演算子で型を渡すとどうなるのでしょうか?

変換演算子が T* の場合、曖昧さが生じます。 f(T&,long) の場合、第2引数にIntegral Promotionが必要であるのに対し f(T*,int) の場合、変換演算子は最初の (@litb に感謝)

その時 addr_impl_ref が効いてきます。C++規格では、変換列は最大で1つのユーザ定義変換を含むことができることを義務付けています。で型を包むことで addr_impl_ref で、すでに変換列を強制的に使用することで、その型に付属する変換演算子を無効にしています"quot;。

したがって f(T&,long) が選択されます(積分処理も行われます)。

その他の型ではどうなるのでしょうか?

このように f(T&,long) のオーバーロードが選択されますが、これはその型が T* パラメータを使用します。

注:Borland互換性に関するファイルの備考から、配列はポインタに減衰せず、参照渡しされる。

このオーバーロードではどうなるのでしょうか?

を適用しないようにしたい。 operator& がオーバーロードされた可能性があるため、この型に変換します。

規格では、以下のことが保証されています。 reinterpret_cast は、この作業に使用することができます(@Matteo Italiaの回答:5.2.10/10を参照してください)。

Boost は constvolatile 修飾子を使うことで、コンパイラの警告を回避することができます(そして、適切に const_cast を削除してください)。

  • キャスト T& から char const volatile&
  • を剥がす。 constvolatile
  • を適用します。 & 演算子を使って、アドレス
  • にキャストバックする。 T*

const / volatile ジャグリングはちょっとした黒魔術ですが、(4つのオーバーロードを提供するのではなく)作業を簡略化することができます。なお T は非限定であるため、もし ghost const& であれば T*ghost const* 従って、修飾語は実際には失われていない。

EDITです。 ポインタのオーバーロードは、関数へのポインタに使用されます。私はまだ、なぜそれが 必要 とはいえ

以下は アイディーン出力 は、これを多少なりとも要約している。