将 unique_ptr 与成员函数指针一起使用时出错

Posted

技术标签:

【中文标题】将 unique_ptr 与成员函数指针一起使用时出错【英文标题】:Error in using unique_ptr with member function pointer 【发布时间】:2013-01-01 00:14:54 【问题描述】:

我有一门课如下

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>)。为什么?谢谢。

【问题讨论】:

错误是什么?此外,不需要整个源代码。 VC2010 给出错误 C2296,非法,左运算符包括 std::unique_ptr<_ty> 您可以编辑问题并将其添加到其中吗? 【参考方案1】:

似乎operator-&gt;*() 运算符没有为std::unique_ptr&lt;T&gt; 重载。 为什么这个操作符没有被定义的原因并不完全清楚,尽管我认为在提出智能指针的时候还没有处理合适的重载的必要机制。 p>

问题是operator-&gt;*()需要处理返回绑定的结果。对于一个简单的成员函数,这是相当简单的,但对于函数来说,这并不完全是微不足道的。这是unique_ptr&lt;T&gt; 类模板的简约变体,它只是显示了实现的样子:

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-&gt;*() 运算符的一个版本进行一些思考。指向成员变量的指针的版本很简单:它只需要返回对应成员的引用。成员函数的版本需要创建一个可调用对象,其中第一个(隐式)参数绑定到正确的对象。

处理任意数量的参数需要使用可变参数。 unique_ptr&lt;T&gt; 的定义也处理带参数的成员函数指针,可能看起来像这样:

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>
    
    ;

【讨论】:

我什至不知道 operator->* 存在!但是为什么 operator-> 不够呢? @user1610015: operator-&gt;() 正在做一些完全不同的事情:它实际上只是通过对operator-&gt;() 的一系列调用,直到最终到达一个普通的指针。指向成员运算符的指针需要提供适当绑定的对象。可以说 -&gt;* 应该是 -&gt; 运算符的应用程序,然后在结果上使用内置的 -&gt;* 运算符,但这不是定义操作的方式(为什么不,我不能立即解释)。【参考方案2】:

-&gt;* 语法是单个运算符(“指向成员的指针”运算符之一)。该运算符可以重载,但std::unique_ptr 不会这样做。

【讨论】:

以上是关于将 unique_ptr 与成员函数指针一起使用时出错的主要内容,如果未能解决你的问题,请参考以下文章

将 unique_ptr 与需要指针指针的接口一起使用到抽象类

指向重载静态成员的函数指针 - 在 unique_ptr 中用作自定义删除器

智能指针之unique_ptr

Fortran:指向抽象成员函数的函数指针

使用 std::unique_ptr.reset() 更改指针下的对象

使用显式定义的默认构造函数将 unique_ptr 的类内成员初始化程序设置为 nullptr 错误