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++ 支持的这些宏观方面如下:
-
实例化相同的源代码以生成不同的行为(机器代码)用于某些其他类型或类型的排列(这是的一个方面参数化 多态),
- 实际上称为模板的“实例化”和预处理器宏的“替换”,但为了方便起见,我将在下文中使用“实例化”;从概念上讲,重新编译或重新解释......
隐式调度(静态或动态)到不同的行为(机器代码)适合正在处理的不同类型的数据。
...根据我在Polymorphism in c++ 的回答,在一些小方面
不同类型的多态性涉及其中一个或两个:
dispatch (2) 可能发生在实例化过程中 (1) 对于模板和预处理器宏 ,
实例化(1)通常发生在调度期间(2)模板(没有匹配的完全专业化)和功能-比如宏(有点循环,虽然宏不会递归扩展)
dispatch (2) 可以发生 without 实例化 (1) 当编译器选择预先存在的函数 overload 或 模板专业化,或者当编译器触发 virtual/dynamic dispatch。
你的代码实际使用了什么?
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 和指针常量)实例化模板的能力,并将它们用于各种事物,而无需改变数据类型,因此不涉及任何多态性。宏更加灵活。
请注意,在 C.R.T.P. 中使用模板。 style NOT 是静态多态性的要求——它是一个示例应用程序。在实例化期间,编译器在将操作与参数指定类型中的实现进行匹配时表现出静态多态性。
术语讨论
很难获得多态性的明确定义。 wikipedia 引用 Bjarne Stroustrup 的在线词汇表“为不同类型的实体提供单一接口”:这意味着 struct X { void f(); }; struct Y { void f(); }; 已经表现出多态性,但恕我直言,当我们使用来自客户端代码的接口对应关系时,我们才会得到多态性,例如template <typename T> void poly(T& t) { t.f(); } 需要为每个实例化静态多态分派到 t.f()。