关于模板专业化和继承的良好实践

时间:2011-10-28 11:55:22

标签: c++ templates template-specialization typetraits

模板专业化不考虑继承层次结构。例如,如果我专门为Base创建模板并使用Derived对其进行实例化,则不会选择专门化(请参阅下面的代码(1))。

这可能是一个主要障碍,因为它有时会导致违反Liskov替代原则。例如,在处理this question时,我注意到我无法使用std::sub_match使用Boost.Range算法,而std::pair则可以使用sub_match。由于pairsub_match公开继承,常识会指示我可以在pair处使用enable_if替换is_base_of,但由于使用模板专精化的特征类,这会失败。

我们可以通过使用部分模板专业化以及(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" } 来解决此问题(请参阅代码(2))。我是否总是喜欢这种解决方案而不是完全专业化,特别是在编写库代码时?我监督这种方法有什么缺点吗?这是您经常使用或经常使用的做法吗?


示例代码

{{1}}

{{1}}

1 个答案:

答案 0 :(得分:13)

enable_if更灵活

我认为您应该更喜欢enable_if方法:它可以支持您需要的所有内容以及更多内容。

E.g。可能存在这样的情况:Derived类是Liskov-Subsitutable for Base,但是你[不能假设/不想要应用]相同的特征/特化是有效的(例如因为Base是POD类,而派生和非POD行为或类似于与类成分完全正交的行为)。

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

混合方法

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

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

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