Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error in using unique_ptr with member function pointer

I have a class as below

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

private:
    int m_key;
};

I test using unique_ptr with member function pointer

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

The first one gives a compilation error (VC2010 gives error C2296, illegal, left operator includes std::unique_ptr<_Ty>). Why? Thanks.

like image 420
user1899020 Avatar asked Jan 01 '13 00:01

user1899020


People also ask

Can you pass unique_ptr to a function?

As Sergey explained, std::unique_ptr is a type and you can pass a std::uniqe_ptr instance to a function by value or by reference. Just note that std::unique_ptr is not copyable but movable. Using unique_ptr in this way both documents and enforces the function call's ownership transfer.

Is unique_ptr a pointer?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.

When should we use unique_ptr?

When to use unique_ptr? Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another.


2 Answers

It seems the operator->*() operator isn't overloaded for std::unique_ptr<T>. The reason why this operator isn't defined isn't entirely clear although I think that at the time when the smart pointers were proposed the necessary mechanics for dealing with the suitable overloads were not in place.

The problem is that operator->*() needs to deal with returning the bound result. For a simple member function this is reasonably simple but for functions it isn't entirely trivial. Here is a minimalistic variation of the unique_ptr<T> class template which just shows the implementation would look like:

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_);
    }
};

This version merely copes with pointers to member variables and pointer to member functions with no arguments. I need to meditate a bit over a version of the operator->*() operator for arbitrary number of arguments. The version for pointer to member variables is trivial: It just needs to return a reference the corresponding member. The version for member functions needs to create a callable object with the first (implicit) parameter being bound to the correct object.

Dealing with an arbitrary number of arguments take a bit of playing with variadic arguments. A definition of unique_ptr<T> also dealing with member functions pointers taking arguments could look something like this:

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());
    }
};

The main trick consists in creating a sequence of suitable placeholders for the arguments. The corresponding helper classes are defined thus:

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>
    {
    };
}
like image 139
Dietmar Kühl Avatar answered Oct 23 '22 14:10

Dietmar Kühl


The ->* syntax is a single operator (one of the "pointer-to-member" operators). This operator can be overloaded, but std::unique_ptr doesn't do this.

like image 2
Kerrek SB Avatar answered Oct 23 '22 12:10

Kerrek SB