Шаблон для специализации шаблонов на основе наследования возможен? - PullRequest
3 голосов
/ 06 мая 2009

Итак, одна проблема, с которой я постоянно сталкиваюсь и для которой нет хорошего решения, - это как обеспечить специализации шаблона, основанные на том, из какого типа получен параметр шаблона. Например, предположим, у меня есть:

template<typename T>
struct implementPersist;

template<typename T>
void persist( T& object )
{
   implementPersist::doPersist( object );
}

Что бы я хотел, чтобы пользователи persist могли предоставлять реализации Implementers :: persist для типов, которые объявлены после вышеперечисленного. Это просто в принципе, но на практике громоздко, но пользователю необходимо предоставить внедряемый список для каждого типа.

Для большей ясности предположим, что у меня есть:

struct Persistent { virtual void myPersist() = 0; };
struct MyClass : public persistent { virtual void MyPersist() { ...implementation...} };

// Persists subclasses of Persistent using myPersist
template<>
struct implementPersist<Persistent>{ void doPersist(Persistent& p) { p->myPersist(); } };

struct X{};

template<>
struct implementPersist<X>{ void doPersist(X& p) { ...implementation...} };


// Persists subclasses of Persistent using boostPersist
struct MyBoostPersistedObject { virtual void boostPersist() = 0 };
struct Z : public MyBoostPersistedObject { virtual void boostPersist() = 0 };

template<>
struct implementPersist<myBoostPersistedObject>{ void boostPersist() { ...implementation... } };

Я намерен предоставить одну реализацию шаблона для всех подклассов Persist , другую для всех подклассов myBoostPersistedObject и другую для разных классов, не входящих в структуры интересных классов (например, различные типы POD). Однако на практике

implementPersist<Persistent>::doPersist

вызывается только в том случае, если :: persist (T &) вызывается, когда T является точно a Постоянным объектом. Это возвращается к (отсутствующему) общему случаю, когда T = myClass. В общем, я хочу иметь возможность специализировать шаблоны на общих принципах, основанных на наследовании. Это немного расстраивает, потому что компиляторы четко знают, как это сделать, и делают это при принятии решения о вызове функций на основе параметров, например,

void persist (Постоянный &); пустота сохраняется (X &); void persist (myBoostPersistedObject &);

Но, насколько я могу судить, подобное сопоставление не может быть выполнено для шаблонов.

Один из обходных путей - сделать что-то вроде:

class persist;

template<typename T, bool hasMyPersistMethod=isDerivedFrom(T,persist)::value >
struct implementPersist;

template<typename T, bool true >
struct implementPersist<T,true>
{
   template<> struct implementPersist<X>{ void doPersist(T& p) { p->myPersist(); } }
};

(см. здесь для isDerivedFrom).

Однако для этого необходимо, чтобы первоначальное объявление Implementers знало о типах классов, обеспечивающих реализации. Я хотел бы что-то более общее.

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

Есть идеи?

Ответы [ 2 ]

3 голосов
/ 06 мая 2009

Да, вы можете сделать это, используя enable_if.

#include <iostream>
#include <boost/type_traits.hpp>
using namespace std;


template <bool Enable, typename T = void>
struct enable_if
{
    typedef T type;
};

template <typename T>
struct enable_if<false, T>
{
};

template <typename T, typename Enable = void>
struct persist_t {};

struct A
{
    virtual void foo() const = 0;
};

template <typename T>
struct persist_t<T, typename enable_if<boost::is_base_of<A, T>::value>::type>
{
    static void persist(T const& x)
    {
        x.foo();
    }
};


struct B : A
{
    virtual void foo() const { cout << "B::foo\n"; }
};

template <typename T>
void persist(T & x)
{
    persist_t<T>::persist(x);
}

int main()
{
    B b;
    persist(b);
}

Boost имеет более приятную реализацию enable_if , я просто предоставил ее здесь для полноты. Boost также имеет пример использования, который очень похож на мой пример выше.

Надеюсь, это поможет.

0 голосов
/ 06 мая 2009

Попробуйте использовать ссылку базового типа для ссылки на производный тип:

MyClass x(...);
Persistent * p = &x;
implementPersist<Persistent> ip;
ip.doPersist(*p);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...