C++ 中是否存在真正的静态多态性?
绝对 - 静态多态性有三种机制:模板、宏和函数重载。
据我了解,C++ 编译器会将单个定义的函数划分为不同数量(取决于不同类型的调用计数)的函数。我是对还是错?
这是一般的想法。被实例化的函数的数量取决于模板参数的排列数量,可以显式指定为function<int>
和function<double>
或 - 对于使用模板参数匹配函数参数的模板 - 自动从函数参数派生,例如:
template <typename T, size_t N>
void f(T (&array)[N])
{ }
double d[2];
f(d); // instantiates/uses f<double, 2>()
您应该在可执行二进制映像中得到每个实例化模板的单个副本。
我想知道我是否可以将上面的代码描述为静态多态性?
并不真地。
背景——多态性和代码生成
我认为对多态性至关重要的要求是:
但是,我们试图通过多态来实现的是更通用的一点:
C++ 支持这些更宏观的方面,如下所示:
实例化相同的源代码,为其他类型或类型排列生成不同的行为(机器代码)是参数多态性的一个方面),
- 实际上称为模板的“实例化”和预处理器宏的“替换”,但为了方便起见,我将在下文中使用“实例化”;从概念上讲,重新编译或重新解释......
隐式分派(静态或动态)到适用于正在处理的不同类型的数据的不同行为(机器代码)。
...并且根据我在 C++ 中的多态性中的回答以一些小的方式
不同类型的多态性涉及其中之一或两者:
分派(2) 可能在模板和预处理器宏的实例化(1)期间发生,
实例化(1)通常发生在模板(没有匹配的完全专业化)和类似函数的宏(循环的种类,尽管宏不递归扩展)的调度(2)期间
当编译器选择预先存在的函数重载或模板专门化,或者编译器触发虚拟/动态调度时,调度(2) 可以在没有 实例化(1) 的情况下发生。
您的代码实际使用什么?
function<int>
并重function<double>
用function
模板代码为每种类型创建不同的代码,因此您将获得如上所示的实例化(1)。但是,您是在硬编码要调用哪个实例化,而不是让编译器根据某个参数的类型隐式选择一个实例化,即在调用时不直接使用隐式调度 ala (2) function
。实际上,function
缺少编译器可用于隐式选择模板实例化的参数。
仅实例化 (1)不足以认为您的代码使用了多态性。尽管如此,您还是实现了方便的代码重用。
那么什么是明确的多态性呢?
为了说明模板如何支持分派 (2) 以及实例化 (1) 并无可争辩地提供“多态性”,请考虑:
template<typename T>
void function(T t)
{
std::cout << typeid(T).name() << std::endl;
}
function(4); // note: int argument, use function<int>(...)
function(12.3); // note: double argument, use function<double>(...)
上面的代码还利用隐式分派到类型适当的代码- 方面“2”。以上 - 多态性。
非类型参数
有趣的是,C++ 提供了使用整数参数(如布尔值int
和指针常量)实例化模板的能力,并将它们用于各种事物,而无需改变数据类型,因此不涉及任何多态性。宏更加灵活。
请注意,在 CRTP 样式中使用模板不是静态多态性的要求 - 这是其示例应用程序。在实例化期间,编译器在将操作与参数指定类型的实现匹配时表现出静态多态性。
术语讨论
很难获得多态性的明确定义。wikipedia 引用 Bjarne Stroustrup 的在线词汇表“为不同类型的实体提供单一接口”:这意味着struct X { void f(); }; struct Y { void f(); };
已经表现出多态性,但是恕我直言,当我们使用来自客户端代码的接口对应关系时,我们只会得到多态性,例如,对于每个实例化都template <typename T> void poly(T& t) { t.f(); }
需要静态多态调度t.f()
.