3

我正在尝试编写一个智能指针包装器(包含 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 添加额外的包装器。)

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

有时,虽然能够从包装器中获取“基本” 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。
  • 正如您所期望的,它仍然有 const 和 non-const 两种风格。

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

我相当确定该解决方案将涉及 SFINAE 和 boost::enable_if; 我有一点尝试让类似的东西发挥作用,但到目前为止还没有多少运气。

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

4

3 回答 3

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删除所有包装器,最后留下 final 。m_ptr

这是演示

于 2012-05-29T09:08:11.843 回答
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;
};
于 2012-05-29T09:11:36.007 回答
0

我想到的最接近的事情是 Bjarne Stroustrup 写的名为Wrapping C++ Member Function Calls的论文。

本文针对前缀和后缀代码对对象的“包装”调用这一老问题提出了一种简单、通用且有效的解决方案。该解决方案也是非侵入式的,适用于现有类,允许使用多个前缀/后缀对,并且可以在 15 行标准 C++ 中实现。还提供了一个强大的包装器版本。效率的主张得到了测量的支持。

于 2012-05-29T08:52:32.510 回答