我主要使用Java,泛型相对较新。我一直读到Java做出了错误的决定,或者。net有更好的实现等等。

那么,c++, c#, Java在泛型上的主要区别是什么?利弊各有哪些?


当前回答

c++模板实际上比c#和Java模板强大得多,因为它们在编译时进行计算,并支持专门化。这允许模板元编程,并使c++编译器等价于图灵机(即在编译过程中,你可以计算任何可以用图灵机计算的东西)。

其他回答

跟进我之前的帖子。

模板是c++在智能感知上如此糟糕的主要原因之一,不管使用的是哪种IDE。由于模板专门化,IDE永远无法真正确定给定成员是否存在。考虑:

template <typename T>
struct X {
    void foo() { }
};

template <>
struct X<int> { };

typedef int my_int_type;

X<my_int_type> a;
a.|

现在,光标位于指定的位置,IDE很难判断成员a是否具有或具有什么。对于其他语言,解析将是简单的,但对于c++,需要在此之前进行相当多的计算。

情况变得更糟。如果my_int_type也定义在类模板中呢?现在它的类型依赖于另一个类型参数。在这里,即使是编译器也会失败。

template <typename T>
struct Y {
    typedef T my_type;
};

X<Y<int>::my_type> b;

经过一些思考,程序员会得出结论,这段代码与上面的相同:Y<int>::my_type解析为int,因此b应该是与a相同的类型,对吗?

错了。当编译器试图解析这条语句时,它实际上还不知道Y<int>::my_type !因此,它不知道这是一个类型。它可以是其他东西,例如成员函数或字段。这可能会导致歧义(尽管在本例中没有),因此编译器会失败。我们必须显式地告诉它我们引用了一个类型名:

X<typename Y<int>::my_type> b;

现在,代码编译完成。要了解这种情况下如何产生歧义,请考虑以下代码:

Y<int>::my_type(123);

这段代码语句完全有效,并告诉c++执行对Y<int>::my_type的函数调用。但是,如果my_type不是函数而是类型,则此语句仍然有效,并执行特殊的强制转换(函数样式强制转换),通常是构造函数调用。编译器无法判断我们的意思,所以我们必须在这里消除歧义。

在Java中,泛型只是编译器级别的,所以你得到:

a = new ArrayList<String>()
a.getClass() => ArrayList

注意,'a'的类型是数组列表,而不是字符串列表。所以香蕉列表的类型等于()猴子列表。

可以这么说。

c++很少使用“泛型”术语。相反,使用“模板”这个词更准确。模板描述了实现通用设计的一种技术。

c++模板与c#和Java实现的模板非常不同,主要有两个原因。第一个原因是c++模板不仅允许编译时类型参数,还允许编译时常量值参数:模板可以作为整数甚至函数签名。这意味着你可以在编译时做一些非常奇怪的事情,例如计算:

template <unsigned int N>
struct product {
    static unsigned int const VALUE = N * product<N - 1>::VALUE;
};

template <>
struct product<1> {
    static unsigned int const VALUE = 1;
};

// Usage:
unsigned int const p5 = product<5>::VALUE;

这段代码还使用了c++模板的另一个显著特性,即模板专门化。代码定义了一个类模板,product,它有一个值参数。它还为该模板定义了一个特化,每当参数的值为1时就使用该特化。这允许我在模板定义上定义递归。我相信这是安德烈·亚历山德雷斯库最先发现的。

模板专门化对于c++很重要,因为它允许数据结构的结构差异。模板作为一个整体是一种跨类型统一接口的方法。然而,尽管这是可取的,但在实现中不能平等对待所有类型。c++模板考虑到了这一点。这与OOP在覆盖虚方法的接口和实现之间的区别非常相似。

c++模板对于它的算法编程范型是必不可少的。例如,几乎所有容器的算法都定义为接受容器类型为模板类型并统一对待它们的函数。实际上,这并不完全正确:c++并不适用于容器,而是适用于由两个迭代器定义的范围,它们分别指向容器的开始部分和结束部分。因此,整个内容都由迭代器限定:begin <= elements < end。

使用迭代器而不是容器是有用的,因为它允许对容器的部分而不是整个进行操作。

c++的另一个显著特征是类模板的部分专门化。这在某种程度上与Haskell和其他函数式语言中参数的模式匹配有关。例如,让我们考虑一个存储元素的类:

template <typename T>
class Store { … }; // (1)

这适用于任何元素类型。但是我们假设,通过应用一些特殊的技巧,我们可以比其他类型更有效地存储指针。我们可以通过部分专门化所有指针类型来做到这一点:

template <typename T>
class Store<T*> { … }; // (2)

现在,每当我们为一种类型实例化容器模板时,都会使用适当的定义:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.

c++模板实际上比c#和Java模板强大得多,因为它们在编译时进行计算,并支持专门化。这允许模板元编程,并使c++编译器等价于图灵机(即在编译过程中,你可以计算任何可以用图灵机计算的东西)。

最大的抱怨是字体擦除。在这种情况下,不在运行时强制执行泛型。这里有一些关于这个主题的Sun文档的链接。

泛型是按类型实现的 Erasure:泛型类型信息为 之后,仅在编译时出现 它被编译器擦除。