对我来说,C++ 模板使用了鸭子类型的思想,对吗?这是否意味着模板类或方法中引用的所有泛型类型都是鸭子类型?
7 回答
对我来说,C++ 模板是鸭子类型的编译时版本。编译器将编译例如 Class,只要您的 Duck 具有所有需要的类型,它就会实例化一个类。
如果某些地方不正确(例如缺少复制构造函数),则编译失败。当您调用具有非鸭子类型的函数时,真正的鸭子打字中的对应物是失败的。在这里它会在运行时发生。
鸭子打字的意思是,“如果它像鸭子一样嘎嘎叫,像鸭子一样走路,那么它就是一只鸭子”。它在计算机科学中没有正式的定义供我们比较 C++。
当然,C++ 与(例如)Python 不同,但它们都有隐式定义接口的概念。用作 Python 函数参数的对象所需的接口是函数对其执行的任何操作。用作 C++ 模板参数的类型所需的接口是模板对该类型的对象所做的任何事情。这就是相似性,也是评估 C++ 模板的依据。
此外,由于模板参数推导,在 C++ 中您可以尝试传递任何旧对象,编译器会判断它是否可以实例化函数模板。
一个区别是,在 C++ 中,如果参数不嘎嘎作响,那么编译器就是对象。在 Python 中,只有运行时对象(并且仅当函数被实际调用时,如果代码中有条件)。这是对象/类型所要求的接口性质的差异——在 C++ 中,模板要求特定表达式有效,或者不需要。在 Python 中,必要的有效表达式可以依赖于早期必要表达式的运行时值。因此,在 Python 中,您可以请求一个可以大声或安静地嘎嘎叫的物体,如果它大声嘎嘎叫,它也需要走路。在 C++ 中,你可以通过条件来做到这一点dynamic_cast
,如果卷是编译时常量,你可以做模板专业化,但你可以'quack_volume()
返回loud
。当然,在 Python 中,所需的接口可能并不是真正“必需的”——如果方法不存在,则行为是抛出异常,如果发生这种情况,可能会记录并保证调用者的行为。
取决于您是否定义“鸭子类型”,以便这种差异意味着 C++ 没有它。
对我来说,C++ 模板使用了鸭子类型的思想,对吗?
不,C++ 模板用于实现通用代码。也就是说,如果您的代码可以使用多种类型,则不必为每种类型复制它。像这样的事情,std::vector
并且std::list
是这方面的明显例子。C++ 模板已被滥用于做其他事情,但通用性是其初衷。
这是否意味着模板类或方法中引用的所有泛型类型都是鸭子类型?
不,它们只是“普通”类型,就像 C++ 中的所有其他类型一样。在模板实际实例化之前,它们是未知的。
但是,模板可用于实现诸如鸭子类型之类的东西。迭代器就是一个例子。考虑这个函数:
template<class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result)
{
while (first!=last) *result++ = *first++;
return result;
}
请注意,该copy
函数可以接受任何类型的参数,以及它实现不等式运算符、取消引用运算符和后缀自增运算符。这可能与您在 C++ 中的鸭子类型一样接近。
不完全是。Duck 类型(动态类型风格)永远不会产生编译时类型错误,因为它们只是没有任何类型。使用模板,在实例化模板之前您没有类型。一旦你这样做了,变量就有不同的类型,你确实会得到编译时错误。
此外,对于鸭子类型,您可以让一个变量指向不同类型的对象,因为变量只是没有类型。这在模板中是不可能的——一旦你实例化它们,变量就会有一个特定的类型。
但是,它们是相似的,因为约束是隐含的:只检查实际使用的特征。与多态指针相反,实际类型并不重要。
是的,有点-例如,如果 typeX
具有AddRef()
,Release()
并且QueryInterface()
具有适当签名的方法,则可以将其用作具有CComPtr
模板类的 COM 对象。但这不是完整的鸭子类型 - 仍然对参数强制执行类型检查。
不,这是一个不同的概念。鸭子类型是一种找出动态类型容器类型的方法。C++ 模板不是动态类型的,它们使用特定类型进行实例化。
维基百科涵盖了这一区别。