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

发布于 2024-10-22 12:49:50 字数 1330 浏览 1 评论 0原文

给定 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 方法来调用 ->操作员?

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

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

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

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

Given class 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;
};

I wanted to cater for situations where T is a boost::shared_ptr.
In this case the only change to class Foo is that it should invoke

m_impl->doSomething();

instead of

m_impl.doSomething();

I ended up defining FooPtr in the same header

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

Now while the approach works for all classes that I want to use with Foo,
the problem is that I have a lot of duplicate code lying around and any changes
I make to Foo, I also have to make to FooPtr.

How can I refactor the code? E.g. Is there any way that I can determine at compile time if T is of type boost::shared_ptr, and then specialise just the bar and fun methods to invoke the -> operator?

Edit:
Thanks for all the answers so far! I just need some time to work through them all and see which solution is the best fit for our software.

Edit 2:
@Matthieu: This is the test code I was using

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

燃情 2024-10-29 12:49:50

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 提供深度复制。

Sylvain wrote a DRY solution, but I don't like abusing inheritance.

Using a wrapper class to uniformize the interface is easy, especially since pointer semantics work so well!

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

Here, relying on boost::optional which provides the OptionalPointee semantics, we nearly get the same behavior than pointers.

One point I'd like to emphasize though, is the difference in the copying behavior. boost::optional provides deep copy.

め可乐爱微笑 2024-10-29 12:49:50
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;
};
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;
};
爺獨霸怡葒院 2024-10-29 12:49:50

您可以编写一个 caller 类模板,其工作是使用语法 obj.f()obj->f()< 来调用函数/code>,基于 obj 的类型。

下面是一个演示此方法的小示例:

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

You can write a caller class template, whose job is to call the function, either using syntax obj.f() or obj->f(), based on the type of obj.

Here is a small example that demonstrates this approach:

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

And this caller class template is used by this sample class:

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

See this online running demo at ideone : http://www.ideone.com/H18n7

--

EDIT:

This is even more generic. Here you can even pass the function which you want to call in caller. Now caller is not hard-coded with the function to be called!

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

迟月 2024-10-29 12:49:50

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

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

I really question whether you should be using a template here at all. Your template parameter has a very clear interface and therefore looks like you should just use an abstract base class.

Do you really need to have an instance? If you do need to change the way the object is represented, this should be done as a separate exercise and not part of the template that uses it.

迷迭香的记忆 2024-10-29 12:49:50

您可以引入另一个中间模板类,如下所示:

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

You can introduce another intermediate template class, something like that:

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

Now, you only have to code the Foo class only once. And you can specialize it for other smart pointers type by doing partial specialization on FooBase.

Edit: You can also use composition instead of having an inheritance relationship between Foo and FooBase (in which case, I'd probably rename it to FooHelper or something like that).

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();
    }
};
秋叶绚丽 2024-10-29 12:49:50

您可以使用部分专业化。

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

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

You can use partial specialization.

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

template<typename T> class Foo<boost::shared_ptr<T>> {
  //... implement specialization here
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文