用方法替换包装智能指针

时间:2012-05-29 08:39:53

标签: c++ templates smart-pointers sfinae

我正在尝试编写一个智能指针包装器(包含一个boost shared_ptr或scoped_ptr或另一个智能指针包装器);每个包装器类型注入一些额外的功能(例如,记录使用,延迟初始化,验证正确的构造/销毁顺序等)但我希望它们对用户尽可能不可见(这样我可以交换/输出包装器只需更改单个typedef,可能还有一些构造函数代码,但不包括任何使用代码。)

正常使用是微不足道的:

template<typename T>
class some_smart_ptr_wrapper
{
public:
    typedef typename T::value_type value_type;
    ...
    value_type* get() { /*do something magic*/ return m_ptr.get(); }
    const value_type* get() const { /*do something magic*/ return m_ptr.get(); }
    // repeat for operator->, operator*, and other desired methods
private:
    T m_ptr;
};
typedef some_smart_ptr_wrapper< boost::shared_ptr<int> > smart_int;
smart_int p;

(现在所有其他代码都可以使用p与shared_ptr无法区分,至少对于已定义的操作,我只需更改typedef即可添加额外的包装。)

只需按照您的预期嵌套它们,就可以同时使用多个包装器,只有声明(有时初始化)变量的代码需要关注。

有时候能够从包装器中获取“basic”shared / scoped_ptr是有用的(特别是将shared_ptr作为参数传递给不需要触发包装器添加的功能的函数);对于单个包装层,它很容易:

    T& getPtr() { return m_ptr; }
    const T& getPtr() const { return m_ptr; }

但是这不能很好地扩展到多个包装层(调用者必须执行p.getPtr().getPtr().getPtr()正确的次数。)

我希望能够做的是声明getPtr():

  • 如果T实现getPtr(),则它返回m_ptr.getPtr()(无论是什么类型)。
  • 如果T没有实现getPtr(),则返回m_ptr。
  • 正如您所期望的那样,它仍然以常量和非常量两种形式提供。

其最终结果是从外部代码单次调用getPtr()会将链“向上”到原始智能指针,因为这将是唯一一个不实现getPtr()的指针。

我很确定该解决方案将涉及SFINAE和boost :: enable_if;我试图让这样的东西起作用,但到目前为止还没有多少运气。

解决方案或完全替代的方法都受到欢迎;请注意,我希望它可以在VC ++ 2008和GCC中工作(即,遗憾的是没有C ++ 11)。

3 个答案:

答案 0 :(得分:1)

(1)声明一个特殊的成员类型名称,其内部是唯一的:

template<typename T>
class some_smart_ptr_wrapper
{
public:
  typedef T smart_type;  // <-- unique name
...
};

(2)在模板函数包装器中包装getPtr()

template<typename T>
class some_smart_ptr_wrapper
{
...
public:
  T& getPtr () { return m_ptr; }

  typename My_sfinae<T>::RealType& getFinalPtr ()
  { return My_sfinae<T>::getPtr(m_ptr); }
...
};

(3)My_sfinae照常实施的地方:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct My_sfinae {
  typedef T RealType;

  static T& getPtr (T &p) { return p; }
};                        //^^^^^^^^^ business logic!
template<typename T>
struct My_sfinae<T, typename void_<typename T::smart_type>::type> {
  typedef typename My_sfinae<typename T::smart_type>::RealType RealType;

  static RealType& getPtr (T &p)
  { return My_sfinae<typename T::smart_type>::getPtr(p.getPtr()); }
};

正如您所看到的,My_sfinae会移除所有包装递归,最后您将获得最终m_ptr

Here is the demo

答案 1 :(得分:1)

实际上,这是一个简单的问题:只需使用重载:)

template <typename T>
boost::scoped_ptr<T>& get_pointer(boost::scoped_ptr<T>& sp) { return sp; }

template <typename T>
boost::shared_ptr<T>& get_pointer(boost::shared_ptr<T>& sp) { return sp; }

// One overload per "wrapper"
template <typename T>
typename get_pointer_type<T>::type& get_pointer(some_wrapper<T>& p) {
    return get_pointer(p.getPtr());
}

然后诀窍是为每个包装器正确地专门化get_pointer_type

template <typename T>
struct get_pointer_type { typedef T type; };

template <typename T>
struct get_pointer_type< some_wrapper<T> > {
    typedef typename get_pointer_type<T>::type type;
};

答案 2 :(得分:0)

最让我想到的是由Bjarne Stroustrup撰写的论文Wrapping C++ Member Function Calls

  

本文提出了一个简单,通用,有效的解决旧问题的方法   ''wrap''以成对的前缀和后缀代码调用对象。解决方案也是   非侵入式,适用于现有类,允许使用多个前缀/后缀对,以及   可以用标准C ++的15个简单行来实现。一个强大的包装版本   也出席了。效率的要求得到了衡量的支持。