我对静态多态性的理解有哪些不足?这是否都与良好的C++风格有关?
静态多态性和运行时多态性是不同的东西,实现不同的目标。它们在技术上都是多态性,因为它们基于某个东西的类型来决定执行哪段代码。运行时多态性将绑定某个东西的类型(因此运行的代码)推迟到运行时,而静态多态性完全在编译时解决。
这导致了每种方法的优缺点。例如,静态多态性可以在编译时检查假设,或者在否则无法编译的选项中进行选择。它还为编译器和优化器提供了大量信息,使其能够内联调用目标和其他信息。但是静态多态性要求在每个翻译单元中都存在实现,可能会导致二进制代码大小膨胀(模板是花哨的复制粘贴),并且不允许这些确定在运行时发生。
例如,请考虑像
std::advance
这样的东西:
template<typename Iterator>
void advance(Iterator& it, ptrdiff_t offset)
{
// If it is a random access iterator:
// it += offset;
// If it is a bidirectional iterator:
// for (; offset < 0; ++offset) --it;
// for (; offset > 0; --offset) ++it;
// Otherwise:
// for (; offset > 0; --offset) ++it;
}
无法使用运行时多态性使其编译。你必须在编译时做出决定。(通常可以使用标签分派等方法来实现)
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, random_access_iterator_tag)
{
it += offset;
}
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, bidirectional_iterator_tag)
{
for (; offset < 0; ++offset) --it;
for (; offset > 0; --offset) ++it;
}
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, forward_iterator_tag)
{
for (; offset > 0; --offset) ++it;
}
template<typename Iterator>
void advance(Iterator& it, ptrdiff_t offset)
{
advance_impl(it, offset, typename iterator_traits<Iterator>::iterator_category());
}
同样地,有些情况下在编译时你真的不知道类型。考虑以下代码:
void DoAndLog(std::ostream& out, int parameter)
{
out << "Logging!";
}
在这里,DoAndLog
并不知道它所得到的ostream
实现的具体情况--而且可能无法在静态上下文中确定将要传递的类型。当然,这可以转化为模板:
template<typename StreamT>
void DoAndLog(StreamT& out, int parameter)
{
out << "Logging!";
}
但这将强制实现
DoAndLog
在头文件中,这可能是不切实际的。它还要求在编译时可见所有可能的
StreamT
实现,这可能并不正确 - 运行时多态可以跨越DLL或SO边界工作(尽管不推荐)。
什么情况下应该使用它?有哪些指导方针?
这就像有人来问你“当我写句子时,应该使用复合句还是简单句?”或者是一位画家说“我应该总是用红色油漆还是蓝色油漆?”这里没有正确的答案,也没有一套可以盲目遵循的规则。您必须查看每种方法的利弊,并决定哪个最适合您特定的问题领域。
至于CRTP,大多数用例都是允许基类以派生类的方式提供某些东西;例如Boost的iterator_facade
。基类需要在内部具有像DerivedClass operator ++(){ /*增量并返回*this*/}
这样的内容 - 在成员函数签名中以派生形式指定。
它可以用于多态目的,但我没有看到太多这样的情况。
Base::interface
而不是Derived::interface
--您只是隐藏了继承的名称。在那里不存在多态性。 - Billy ONeal