5

私は以下のようなクラスを持っています

class A
{
public:
    A(int key)          : m_key(key) {}
    int Key() const     {return m_key;}

private:
    int m_key;
};

メンバー関数ポインターで unique_ptr を使用してテストします

int (A::*MemFun)() const;
MemFun = &A::Key;
( std::unique_ptr<A>(new A(10))       ->*MemFun ) (); // Error C2296
( std::unique_ptr<A>(new A(10)).get() ->*MemFun ) (); // okay
(*std::unique_ptr<A>(new A(10))        .*MemFun ) (); // okay

最初のものはコンパイル エラーを返します (VC2010 ではエラー C2296、違法、左演算子には std::unique_ptr<_Ty> が含まれます)。なんで?ありがとう。

4

2 に答える 2

7

operator->*()演算子が に対してオーバーロードされていないようですstd::unique_ptr<T>この演算子が定義されていない理由は完全には明らかではありませんが、スマート ポインターが提案された時点では、適切なオーバーロードを処理するために必要なメカニズムが整っていなかったと思います。

問題はoperator->*()、バインドされた結果を返すことに対処する必要があることです。単純なメンバー関数の場合、これはかなり単純ですが、関数の場合は完全に簡単ではありません。unique_ptr<T>以下は、実装が次のようになることを示すクラス テンプレートの最小限のバリエーションです。

template <typename T>
struct unique_ptr
{
    T* p_;
    unique_ptr(T* p): p_(p) {}
    T* operator->() { return this->p_; }
    template <typename R>
    R& operator->*(R T::*mem) { return this->p_->*mem; }
    template <typename R>
    auto operator->*(R (T::*mem)()) ->decltype(std::bind(mem, this->p_))
    {
        return std::bind(mem, this->p_);
    }
};

このバージョンは、メンバ変数へのポインタとメンバ関数へのポインタを引数なしで処理するだけです。operator->*()任意の数の引数について、演算子のバージョンについて少し熟考する必要があります。メンバー変数へのポインターのバージョンは自明です: 対応するメンバーへの参照を返す必要があるだけです。メンバー関数のバージョンでは、最初の (暗黙の) パラメーターが正しいオブジェクトにバインドされた呼び出し可能なオブジェクトを作成する必要があります。

任意の数の引数を処理するには、可変引数を少しいじる必要があります。引数をとるメンバー関数ポインターも処理する定義は、次のunique_ptr<T>ようになります。

template <typename T>
struct unique_ptr
{
private:
    T* p_;
    template <typename R, typename... A, int... I>
    auto bind_members(R (T::*mem)(A...), indices<I...>)
        -> decltype(std::bind(mem, this->p_, placeholder<I + 1>()...))
    {
        return std::bind(mem, this->p_, placeholder<I + 1>()...);
    }

public:
    unique_ptr(T* p): p_(p) {}
    T* operator->() const { return this->p_; }
    template <typename R>
    R& operator->*(R T::*mem) { return this->p_->*mem; }
    template <typename R>
    auto operator->*(R (T::*mem)()) ->decltype(std::bind(mem, this->p_))
    {
        return std::bind(mem, this->p_);
    }
    template <typename R, typename... A>
    auto operator->*(R (T::*mem)(A...))
        -> decltype(this->bind_members(mem,
                typename indices<sizeof...(A) - 1>::type())) {
        return this->bind_members(mem,
            typename indices<sizeof...(A) - 1>::type());
    }
};

主なトリックは、引数に適した一連のプレースホルダーを作成することです。対応するヘルパー クラスは次のように定義されます。

template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
    typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
    typedef typename indices<Index - 1, Index, Indices...>::type type;
};

template <int I>
struct placeholder
    : std::integral_constant<int, I>
{
};

namespace std
{
    template <int I>
    struct is_placeholder<placeholder<I>>
        : std::integral_constant<bool, true>
    {
    };
}
于 2013-01-01T00:42:34.193 に答える
2

->*構文は単一の演算子 (「メンバーへのポインター」演算子の 1 つ) です。この演算子はオーバーロードできますが、これstd::unique_ptrは行いません。

于 2013-01-01T00:42:33.750 に答える