Pynote

Python、機械学習、画像処理について

C++ - Address-of イディオム

意図

operator&() をオーバーライドした場合、オブジェクトのポインタを Address-of 演算子で取得できなくなる。
その場合でもオブジェクトのポインタを得る方法が Address-of イディオムである。

class NonAddressable
{
public:
    // 単行演算子&をオーバーロード
    double operator&() const;
};

int main()
{
    NonAddressable obj;
    NonAddressable* objPtr = &obj;  // コンパイルエラー
    // C2440: '初期化中': 'double' から 'NonAddressable *' に変換できません。
}

コード

template <class T>
T* addressof(T& v)
{
    return reinterpret_cast<T*>(
               &const_cast<char&>(
                    reinterpret_cast<const volatile char&>(v)));
}

class NonAddressable
{
public:
    double operator&() const;
};

int main()
{
    NonAddressable obj;
    NonAddressable* objPtr = addressof(obj);  // OK
}

1. reinterpret_cast で T& 型を const volatile char& 型にする。

const volatile が付いている理由

reinterpret_cast は cv-qualifier を付与することはできるが、外すことはできない。
型T が const A/volatile A/const volatile A/A のいずれの場合でも対応できるよう cv-qualifier を両方つけてしまう。

int main()
{
    {
        const NonAddressable obj;
        reinterpret_cast< NonAddressable& >(obj);  // コンパイルエラー const は外せない
        // C2440: 'reinterpret_cast': 'const NonAddressable' から 'NonAddressable &' に変換できません。
    }
    {
        volatile NonAddressable obj;
        reinterpret_cast< NonAddressable& >(obj);  // コンパイルエラー volatile は外せない
        // C2440: 'reinterpret_cast': 'volatile NonAddressable' から 'NonAddressable &' に変換できません。
    }
}
int main()
{
    {
        const NonAddressable obj;
        reinterpret_cast<const volatile NonAddressable&>( obj ); // OK
    }
    {
        volatile NonAddressable obj;
        reinterpret_cast<const volatile NonAddressable&>( obj ); // OK
    }
    {
        const volatile NonAddressable obj;
        reinterpret_cast<const volatile NonAddressable&>( obj ); // OK
    }
    {
        NonAddressable obj;
        reinterpret_cast<const volatile NonAddressable&>( obj ); // OK
    }
}


char 型にする理由

char は primitive 型なので、operator&() をオーバーロードすることはできない。
そのため、Address-of 演算子で確実にアドレスを得ることができる。

2. const_cast で const volatile char& を char& にする。
const_cast でのみ cv-qualifier を外すことができる。

3. char* を得る。
4. reinterpret_cast で char* を T* に変換する。

型T は const A/volatile A/const volatile A/A の場合がある。
reinterpret_cast は cv-qualifier をつけることはできるので、途中で const と volatile を外した場合でも当初渡した型のポインタに戻せる。

使用例

std::addressof() で提供されている。(C++11)

備考

operator&() でポインタが取得できることを期待するのが普通なので、その演算子をオーバーライドするクラスを設計することがそもそも悪い。