【问题标题】:Is it possible to derive from and "respecialize" a specialized class with a type that derives from the specializing type?是否可以从具有派生自特化类型的类型的特化类派生和“重新特化”?
【发布时间】:2014-06-25 10:28:25
【问题描述】:

简化问题

我有以下几点:

class Foo {};
class Bar : public Foo {};

template <class T>
class TemplatedClass   : public T {};
class SpecializedClass : public TemplatedClass<Foo> {};

我需要一个新课程,

class RespecializedClass : public TemplatedClass<Bar> {};

但是这个新类在各方面都与SpecializedClass 相同,只是它专门针对Bar,一个派生自Foo 的类。 可以简单地复制粘贴SpecializedClass,但是我必须维护两个应该相同的代码行。

有没有办法从SpecializedClass派生RespecializedClass,但进一步将模板数据类型特化为子数据类型?

原始问题(更接近我的实际问题)

我得到了以下类层次结构:

class X {};

class A {};

template <class T>
class B : public T, public A {};

class C : public B<X> {};

class D : public C {};

我无法触及XABCD 背后的任何来源。我只能从它们派生和扩展。

现在,我从X 派生了一个新类——称之为小写x。 (我通常不会在一个设置中使用Xx,但在这种情况下,视觉相似性在这种情况下很有帮助。)

class x : public X {};

我需要的是一个与D 相同的类,但模板是x 而不是X

当然,我可以简单地复制粘贴 CD 并将它们专门用于 x,如下所示:

class c : public B<x> {};
class d : public c {};

但是每当CD 被修改时,我都必须维护这个代码行。

有没有办法做类似的事情

class d : public D, public B<x> {};

要在层次结构中“重新专门化”模板的数据类型?

注意

无需阅读本段;事实上,它可能比启发更令人困惑。 我需要在x 而不是X 上模板化d 的原因是xX 封装了数据库表,而B 有被写来更新它模板化的任何表。为了更接近事实,我正在使用的基础架构为我们所有的数据库表自动生成封装类。在数据库字典中,可以配置从另一个表继承所有字段和键的“派生”表,然后使用附加字段和键扩展该表。基础设施不仅为这些“派生”表自动生成封装类,而且实际上将它们表示为它们各自父类的派生类。所以,事实上,我实际上并不是在编码 x——它是为我自动生成的,作为X 的子类。现在我可以更准确地说,我需要的是一个类,它可以执行所有操作 D 确实,但在数据库表x 上操作,而不是X

【问题讨论】:

  • 您为什么不想使用一个类型参数 (class T) 制作 CD(或其副本)类模板?
  • 不幸的是,XABCD 都是我公司基础架构的一部分。我可以派生和扩展这些类,但不能修改它们。除非我误会你了。
  • 所以,从本质上讲,您希望 d 和 D 从同一个接口派生,以便能够在某些算法中使用 d 代替 D?
  • @didierc - 我认为这是一种有效的表达方式。但我不仅想要相同的界面,还想要相同的代码。也就是说,任何适用于X 的代码也应该适用于x,对吧?我实际上不需要修改D 的代码。我只需要T 是专门的,因为B 中有一些代码(同样,我无法触及)如果它专门用于X,它将写入一个数据库表,或者如果它专门用于另一个数据库表x。 (有关详细信息,请参阅注释。)
  • 任何代码在 d 和 D 上工作的唯一方法是它们共享相同的祖先(也可以是任何一个类)并且该代码在那个共同的祖先上工作。由于您的框架不关心表“共性”,并且您无法在 C++ 中开箱即用(afaik):您必须实现一个适配器类,该类将公开通用接口并包装 D 或 d,或者编写模板该界面上的代码。

标签: c++ templates inheritance template-specialization derived-class


【解决方案1】:

有没有办法从 SpecializedClass 派生 RespecializedClass,但进一步将模板数据类型特化为子数据类型?

否。 至少,没有你不能触及SpecializedClass 的实现的约束。 如果你可以改变它,你应该像这样改变它:

旧代码:

template <class T>
class TemplatedClass   : public T {};
class SpecializedClass : public TemplatedClass<Foo> {};

新代码:

template <class T>
class TemplatedClass   : public T {};
template<type Base = Foo> // << HERE
class SpecializedClass : public TemplatedClass<Base> {};

由于您无法做到这一点,因此您有以下可能性:

  • 滚动你自己的SpecializedClass:如你所说,将SpecializedClass复制到你自己的专业中(类似于上面的“新代码”,然后添加:using RespecializedClass = SpecializedClass&lt;Bar&gt;;)。然后,使用新的 SpecializedClass 维护所有代码(直接将其称为 RespecializedClass?:))并忽略原始库提供的代码。不过,您必须将原始代码中的修复和补丁传播到您自己的副本中:(

  • 向公司的原始维护人员提出补丁,说明您的具体需求。如果您为Base 提供默认模板参数,则整个公司的客户端代码无需任何更改即可工作。

【讨论】:

  • 我制作了一个我无法触摸的代码的本地副本,只是为了尝试这个解决方案。但是我遇到了一些问题。首先,我认为您的意思是typename,而不是type?其次,我现在只修改一行——以前是class SpecializedClass : public TemplatedClass&lt;Foo&gt;——现在是template&lt;typename Base = Foo&gt; class SpecializedClass : public TemplatedClass&lt;Base&gt;。但是我得到编译错误,“错误:没有为模板提供参数。”,在这些行:const char *SpecializedClass::className = "SpecializedClass";。是不是因为模板类不能有静态成员?
  • 你必须像这样实例化静态成员:template&lt;typename Base&gt; const char * SpecializedClass&lt;Base&gt;::className = "SpecializedClass";.
【解决方案2】:

如果您的目标是最大程度地减少代码重复,我认为最简单的方法是将您的可共享代码编写为模板,而无需干预您的 ORM 库继承树。您的模板将能够对它们操作的类型做出您需要的假设(界面类似于D 之一)。

现在,关于问题的简化陈述,如果您希望重新专门化的类的方法在 TemplatedClass 中声明为虚拟,那么从 TemplatedClass&lt;Bar&gt;SpecializedClass 手动派生应该不是问题SpecializedClass,并做正确的内部管道。否则,您将无法使用RespecializedClass 代替SpecializedClass。剩下的解决方案是为这些创建一个适配器类,并为适配器编写代码,或者使用本文开头的模板化代码解决方案。

【讨论】:

  • 感谢您的见解!还在考虑。
  • 谢谢,我明白你的意思。我的印象是某些东西必须是可能的(这是模板效果)。我也会考虑的。
【解决方案3】:

鉴于您的层次结构:

class X {};
class A {};
template <class T> class B : public T, public A {};
class C : public B<X> {};
class D : public C {};

你可以让 B 的每个子类也成为一个类模板:

template <class T> class CT: public B<T> {};
template <class T> class DT: public C<T> {};

类模板CTDT 由您控制,因此您可以将它们专门用于T==x。只是为了测试,定义一些与旧类 CD 相同的模板别名:

using class CX = C<X>; // equal to your old C
using class DX = D<X>; // equal to your old D

然后你需要另外两个 typedef 来获得你需要的类:

using class Cx = C<x>;
using class Dx = D<x>;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多