关于模板专业化和继承的良好实践 [英] Good practices regarding template specialization and inheritance
问题描述
模板特化不考虑继承层次结构。例如,如果我专门为 Base
创建一个模板,并用 Derived
实例化它,则不会选择专业化
Template specialization does not take into account inheritance hierarchy. For example, if I specialize a template for Base
and instantiate it with Derived
, the specialization will not be chosen (see code (1) below).
这可能是一个主要的障碍,因为它有时会导致违反Liskov替代原则。例如,在处理此问题时,我注意到我无法使用Boost.Range算法与 std :: sub_match
,而我可以用 std :: pair
。由于 sub_match
从 pair
公开继承,常识会指示我可以用 sub_match
到处使用对
,但是由于trait类使用模板专门化而失败。
This can be a major hindrance, because it sometimes lead to violation of the Liskov substitution principle. For instance, while working on this question, I noticed that I could not use Boost.Range algorithms with std::sub_match
while I could with std::pair
. Since sub_match
inherits publicly from pair
, common sense would dictate that I could substitute a sub_match
everywhere a pair
is used, but this fails due to trait classes using template specialization.
我们可以通过使用部分模板专门化以及 enable_if
和 is_base_of
(见代码(2))来克服这个问题。我应该总是喜欢这个解决方案完全专业化,特别是当编写库代码?这种方法有什么缺点,我已经监督?
We can overcome this issue by using partial template specialization along with enable_if
and is_base_of
(see code (2)). Should I always favor this solution over full specialization, especially when writing library code? Are there any drawbacks to this approach that I have overseen? Is it a practice that you use or have seen used often?
示例代码
(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"
}
推荐答案
enable_if
更灵活
我认为你应该更喜欢enable_if方法:它启用了你可能需要的一切,更多。
enable_if
is more flexible
I think you should really prefer the enable_if approach: it enables everything that you could require and more.
例如可能有这样的情况:派生类是基于Liskov可替代的, 你不能假设/不想应用相同的traits / specializations是有效的(例如因为Base是POD类,而Derived和非POD行为或类似与类构成完全正交的行为)。
E.g. there might be cases where a Derived class is Liskov-Subsitutable for a Base, but you [cannot assume/donot want to apply] the same traits/specializations to be valid (e.g. because the Base is POD class, whereas Derived ands non-POD behaviour or somehting like that that is completely orthogonal to class composition).
enable_if
让你有能力准确地定义条件。
enable_if
gives you the power to define exactly the conditions.
您还可以通过实现一个traits类来实现一些中间体,从通用特性。 '自定义'traits可以使用enable_if和元编程技术来应用traits作为你想要的多态。这样,你的实际实现不必重复一些复杂的enable_if / dispatch dance,而是可以简单地使用custom-traits类(隐藏复杂性)。
You could also achieve some middleground by implementing a traits class that derives some application-specific traits from general-purpose traits. The 'custom' traits could use the enable_if and meta-programming techniques to apply traits as polymorphically as you desire. That way, your actual implementations do not have to repeat some complicated enable_if/dispatch dance but instead can simply consume the custom-traits class (that hides the complexity).
我认为一些(许多?)Boost库使用混合方法(我已经看到它在一些容量,它桥梁如fusion / mpl,我认为也各种迭代器
I think some (many?) Boost libraries use the hybrid approach (I've seen it in some capacity where it bridges e.g. fusion/mpl, I think also various iterator traits in Spirit).
我个人喜欢这种方法,因为它可以有效地将'plumbing'与库的核心业务隔离开来,使维护和文档(!)很容易。
I personally like this approach because it can effectively isolate the 'plumbing' from the core-business of a library, making maintenance and documentation (!) a lot easier.
这篇关于关于模板专业化和继承的良好实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!