【问题标题】:Pattern to specialize templates based on inheritance possible?可以基于继承专门化模板的模式吗?
【发布时间】:2009-05-06 04:49:28
【问题描述】:

所以,我经常遇到的一个问题模式是,如何提供基于模板参数派生的类型的模板特化。 例如,假设我有:

template<typename T>
struct implementPersist;

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

我希望persist 的用户能够为在上述之后声明的类型提供implementPersist::persist 的实现。这在原则上很简单,但在实践中很麻烦,但用户需要为每种类型提供一个 implementPersist。

为了更清楚,假设我有:

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 恰好是一个 Persistent 对象。它回退到 T=myClass 的(缺失的)通用情况。一般来说,我希望能够以基于继承的通用方式专门化模板。这有点令人沮丧,因为显然编译器知道如何做到这一点,并且在决定基于参数调用函数时这样做,例如

void 持久化(持久化&); 无效坚持(X&); 无效持久(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,请参见 here)。

但是,这要求implementPersist 的初始声明知道提供实现的类的类型。我想要更通用的东西。

我经常发现这种模式的用途,以避免为我的系统中的每个类添加明确的专业化。

有什么想法吗?

【问题讨论】:

  • 我无法解释为什么您的编译器不会进行您想要的类型推断。但是,我的经验与你的一致,模板和继承似乎不能很好地结合在一起。

标签: c++ templates


【解决方案1】:

是的,您可以使用 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 还有一个使用示例,与我上面的示例非常相似。

希望对您有所帮助。

【讨论】:

  • enable_if 的使用看起来不正确。您通常会将其作为返回类型(或作为构造函数中的冗余默认参数)。你有类似 boost 示例的链接吗?
  • 是的,点击上面的 enable_if。无论如何,这里是:boost.org/doc/libs/1%5F39%5F0/libs/utility/enable%5Fif.html
  • 谢谢 - 我不知道这种用法
【解决方案2】:

尝试使用基类型的引用来引用派生类型:

MyClass x(...);
Persistent * p = &x;
implementPersist<Persistent> ip;
ip.doPersist(*p);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-06
    • 2013-01-08
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多