Рефакторинг шаблона класса c ++ на основе типа шаблона - PullRequest
3 голосов
/ 16 марта 2011

Дан класс 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 для вызова оператора ->?

Edit: Спасибо за все ответы до сих пор! Мне просто нужно время, чтобы обойти их все и выяснить, какое решение лучше всего подходит для нашего программного обеспечения.

Редактировать 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 ]

4 голосов
/ 16 марта 2011

Сильвен написал СУХОЕ решение, но я не люблю злоупотреблять наследованием.

Использовать класс-оболочку для унификации интерфейса легко, тем более что семантика указателей работает так хорошо!

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

Здесь, опираясь на boost::optional, который обеспечивает семантику OptionalPointee, мы почти получаем то же поведение, что и указатели.

Хотя я бы хотел подчеркнуть один момент - это разница в поведении при копировании. boost::optional обеспечивает глубокое копирование.

2 голосов
/ 16 марта 2011

Вы можете написать шаблон класса caller, задачей которого является вызов функции, используя синтаксис obj.f() или obj->f(), в зависимости от типа 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

2 голосов
/ 16 марта 2011
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;
};
1 голос
/ 16 марта 2011

Я действительно задаюсь вопросом, следует ли вам вообще использовать шаблон здесь. У вашего параметра шаблона очень четкий интерфейс и, следовательно, похоже, что вы должны просто использовать абстрактный базовый класс.

Вам действительно нужен экземпляр? Если вам нужно изменить способ представления объекта, это следует сделать как отдельное упражнение, а не как часть шаблона, который его использует.

1 голос
/ 16 марта 2011

Вы можете ввести другой промежуточный шаблонный класс, что-то вроде этого:

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.

Редактировать: вы также можете использовать композицию вместо отношения наследования между Foo и FooBase (в этом случаеЯ бы, вероятно, переименовал его в 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();
    }
};
0 голосов
/ 16 марта 2011

Вы можете использовать частичную специализацию.

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

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