基于模板类型重构c ++模板类

时间:2011-03-16 11:59:02

标签: c++ templates refactoring shared-ptr

给出Foo类

template <typename T>
class Foo
{
public:

  ...other methods..

  void bar()
  {
    ...
    m_impl.doSomething();
    ...
  }

  void fun()
  {
    ...
    m_impl.doSomethingElse();
    ...
  }

  void fubar()
  {
    ...
  }

private:
  T m_impl;
};

我想迎合T是boost :: shared_ptr的情况。 在这种情况下,对Foo类的唯一更改是它应该调用

m_impl->doSomething();

而不是

m_impl.doSomething();

我最终在相同的标题中定义了FooPtr

template <typename T>
class FooPtr
{
public:
  ...other methods..

  void bar()
  {
    ...
    m_pImpl->doSomething();
    ...
  }

  void fun()
  {
    ...
    m_pImpl->doSomethingElse();
    ...
  }

  void fubar()
  {
    ...
  }

private:
  boost::shared_ptr<T> m_pImpl;
};

现在虽然该方法适用于我想要与Foo一起使用的所有类, 问题是我有很多重复的代码和任何变化 我向Foo做,我也要做到FooPtr。

如何重构代码?例如。如果T是boost :: shared_ptr类型,有什么方法可以在编译时确定,然后专门调用bar和fun方法来调用 - &gt;操作

编辑: 谢谢你到目前为止的所有答案!我只需要一些时间来完成它们,看看哪种解决方案最适合我们的软件。

编辑2: @Matthieu:这是我使用的测试代码

class FooImpl
{
public:
  void doIt()
  {
    cout << "A" << std::endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  Foo<FooImpl> foo;
  foo.doSomething();
  return 0;
}

6 个答案:

答案 0 :(得分:4)

Sylvain写了一个DRY解决方案,但我不喜欢滥用继承。

使用包装器类来简化接口很容易,特别是因为指针语义工作得很好!

namespace details {
  template <typename T>
  struct FooDeducer {
    typedef boost::optional<T> type;
  };

  template <typename T>
  struct FooDeducer< T* > {
    typedef T* type;
  };

  template <typename T>
  struct FooDeducer< boost::shared_ptr<T> > {
    typedef boost::shared_ptr<T> type;
  };
} // namespace details

template <typename T>
class Foo {
public:
  // methods
  void doSomething() { impl->doIt(); }

private:
  typedef typename details::FooDeducer<T>::type Type;
  Type impl;
};

在这里,依靠提供OptionalPointee语义的boost::optional,我们几乎得到与指针相同的行为。

我想强调的一点是复制行为的差异。 boost::optional提供深层复制。

答案 1 :(得分:2)

class A
{
public:
    void doSomething() {}
};
template <typename T>
class Foo
{
public:
  void bar()
  {
    Impl(m_impl).doSomething();
  }

private:
    template<typename P>
    P& Impl(P* e)
    {
        return *e;
    }
    template<typename P>
    P& Impl(std::shared_ptr<P> e)
    {
        return *e;
    }
    template<typename P>
    P& Impl(P& e)
    {
        return e;
    }
  T m_impl;
};

答案 2 :(得分:2)

您可以根据caller的类型编写obj.f()类模板,其作用是使用语法obj->f()obj调用该函数。< / p>

这是一个展示这种方法的小例子:

template<typename T>
struct caller
{
    static void call(T &obj) {  obj.f(); } //uses obj.f() syntax
};

template<typename T>
struct caller<T*>
{
    static void call(T* obj) {  obj->f(); } //uses obj->f() syntax
};

此示例类使用此caller类模板:

template<typename T>
struct X
{
   T obj;
   X(T o) : obj(o) {}
   void h()
   {
        caller<T>::call(obj); //this selects the appropriate syntax!
   }
};

在ideone上查看此在线运行演示:http://www.ideone.com/H18n7

-

编辑:

这更通用。在这里,您甚至可以在caller中传递您要呼叫的功能。现在caller没有使用要调用的函数进行硬编码!

http://www.ideone.com/83H52

答案 3 :(得分:1)

您可以引入另一个中间模板类,类似于:

template < typename T >
class FooBase
{
private:
    T m_impl;

protected:
    T& impl() { return m_impl; }
};

template < typename T >
class FooBase< boost::shared_ptr< T > >
{
private:
    boost::shared_ptr< T > m_impl;

protected:
    T& impl() { return *(m_impl.operator ->()); }
};

template < typename T >
class Foo : protected FooBase< T >
{
public:
    void bar()
    {
        impl().DoSomething();
    }
};

现在,您只需要对Foo类进行一次编码。并且您可以通过对FooBase进行部分特化来专门化其他智能指针类型。

编辑:您也可以使用合成而不是FooFooBase之间的继承关系(在这种情况下,我可能会将其重命名为FooHelper或类似的东西)

template < typename T >
class FooHelper
{
private:
    T m_impl;

public:
    T& impl() { return m_impl; }
};

template < typename T >
class FooHelper< boost::shared_ptr< T > >
{
private:
    boost::shared_ptr< T > m_impl;

public:
    T& impl() { return *(m_impl.operator ->()); }
};

template < typename T >
class Foo
{
private:
    FooHelper< T > m_helper;

public:
    void bar()
    {
        m_helper.impl().DoSomething();
    }
};

答案 4 :(得分:1)

我真的怀疑你是否应该在这里使用模板。您的模板参数具有非常清晰的界面,因此您应该只使用抽象基类。

你真的需要一个实例吗?如果确实需要更改对象的表示方式,则应将其作为单独的练习完成,而不是使用它的模板的一部分。

答案 5 :(得分:0)

您可以使用部分专业化。

template <typename T>
class Foo
{
public:
  //...
};

template<typename T> class Foo<boost::shared_ptr<T>> {
  //... implement specialization here
};