【问题标题】:Good practices regarding template specialization and inheritance关于模板专业化和继承的良好实践
【发布时间】:2011-12-17 06:00:50
【问题描述】:

模板特化不考虑继承层次结构。例如,如果我为Base 特化一个模板并用Derived 实例化它,则不会选择特化(参见下面的代码(1))。

这可能是一个主要障碍,因为它有时会导致违反 Liskov 替换原则。例如,在处理this question 时,我注意到我不能将Boost.Range 算法与std::sub_match 一起使用,而我可以与std::pair 一起使用。由于sub_match 公开继承自pair,因此常识表明我可以在使用pair 的任何地方替换sub_match,但这会由于使用模板专业化的特征类而失败。

我们可以通过使用部分模板专业化以及enable_ifis_base_of 来克服这个问题(参见代码(2))。我是否应该始终支持这种解决方案而不是完全专业化,尤其是在编写库代码时?我监督过的这种方法有什么缺点吗?您是否经常使用或经常看到这种做法?


示例代码

(1)
#include <iostream>

struct Base {};
struct Derived : public Base {};

template < typename T >
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template <>
struct Foo< Base >
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo<Derived>::f(); // prints "Default"
}

(2)
#include <type_traits>
#include <iostream>

struct Base {};
struct Derived : public Base {};

template <typename T, typename Enable = void>
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template <typename T>
struct Foo<
    T, typename 
    std::enable_if< std::is_base_of< Base, T >::value >::type
>
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo<Derived>::f(); // prints "Base"
}

【问题讨论】:

  • +1 很好的问题和动机。
  • +1。确实是一个很好的问题。

标签: c++ templates template-specialization typetraits


【解决方案1】:

enable_if 更灵活

我认为您应该更喜欢 enable_if 方法:它可以实现您可能需要的一切以及更多。

例如在某些情况下,派生类可以 Liskov-Subsitutable 替代 Base,但是您[不能假设/不想应用]相同的特征/专业化是有效的(例如,因为 Base 是 POD 类,而派生和非 POD 行为或类似的行为与类组合完全正交)。

enable_if 让您能够准确定义条件。

混合方法

您还可以通过实现一个从通用特征派生一些特定于应用程序的特征的特征类来实现一些中间立场。 “自定义”特征可以使用 enable_if 和元编程技术来应用您想要的多态特征。这样,您的实际实现不必重复一些复杂的 enable_if/dispatch 舞蹈,而是可以简单地使用 custom-traits 类(隐藏了复杂性)。

我认为一些(很多?)Boost 库使用混合方法(我已经看到它以某种方式连接,例如 fusion/mpl,我认为也是 Spirit 中的各种迭代器特征)。

我个人喜欢这种方法,因为它可以有效地将“管道”与图书馆的核心业务隔离开来,从而使维护和文档(!)更容易。

【讨论】:

  • 很好的答案,谢谢。我认为您唤起的混合方法可能与tag dispatching 有关:使用多态特征类来获取标签,然后使用基于标签的特化或重载(即使标签有时可以是多态的,因此它们可能应该保留用于重载...)。
  • @sehe:例如你更喜欢 mytraits::eligible_for_this_specialization::type 而不是 std::enable_if::value >::type ?
  • @user396672:不,我宁愿完全避免 enable_if 并直接使用mytraits。您可以在自定义特征类中“隐藏”条件(可能基于 enable_if)。
  • @user396672:考虑这一切的一种方法是将基本模板视为“模板方法”(设计模式),它使用特征类来提供可定制的钩子。如果需要自定义基本模板的某些部分,她只需要专门化(“覆盖”)相应的特征。如果必须更改整个结构/行为,那么仍然可以专门化(“覆盖”)基本模板。
  • 我认为一个好的做法是将所有可能需要特化的东西封装成特征(可能会被多态特化),并鼓励用户特化特征而不是基本模板(几乎像模板方法通常不是虚拟的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-17
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多