c++模板和Java/ c#泛型之间的区别是什么?限制是什么?

本文关键字:是什么 区别 之间 泛型 Java c++ | 更新日期: 2023-09-27 18:07:44

我在这里读了一篇有趣的文章/帖子/讨论,我有以下问题:

    Java/c#泛型的限制是什么?
  • Java/c#泛型不可能实现的c++模板是什么?

编辑1 Eric Lippert的更多推荐问题

  • 有哪些模式可以用c#泛型,但不可能用c++模板?
  • c#的真正泛型类型和Java的类型擦除泛型类型有什么区别?

c++模板和Java/ c#泛型之间的区别是什么?限制是什么?

首先,您可能想要阅读我2009年关于这个主题的文章。

在我看来,c++模板和c#泛型之间的主要区别在于c++模板实际上在构造模板时完全重新编译了代码。c++方法的优缺点有很多:
  • PRO:你可以有效地创建约束,如"类型参数T必须有一个加法运算符";

  • 如果代码中包含一对相互添加的t,那么如果使用不允许添加的类型参数构造模板,则模板将无法编译。
  • CON:您可能会意外创建未记录的约束,如"类型参数T必须有一个加法运算符"。

在c#中,你必须说明哪些约束对用户有帮助,但你仅限于一小部分可能的约束:接口、基类、值与引用类型和默认构造函数约束,仅此而已。

  • PRO:对于两种不同的结构,语义分析可能完全不同。

  • CON:对于两种不同的结构,语义分析可能完全不同。如果你不希望这样,这是一个等待发生的错误

在c#中,无论构造多少次类型,语义分析都是一次完成的,因此需要使用任何满足约束的类型参数,而不仅仅是实际提供的类型参数。

  • PRO:您只生成您需要的结构的代码。

  • CON:您为使用的所有结构生成代码。

模板可能导致代码段变大。在c#中,泛型类型的IL只生成一次,然后在运行时抖动对程序使用的所有类型进行编码。这有一个小的性能成本,但抖动实际上只为所有引用类型参数生成一次代码,这一事实在一定程度上减轻了性能成本。因此,如果您有List<object>List<string>,那么编译代码只生成一次,并用于两者。List<int>List<short>则相反,两次抛出代码。

  • PRO:当你使用模板库时,源代码就在那里。

  • CON:要使用模板库,你必须有源代码。

在c#中,泛型类型是一等类型。如果你把它们放在一个库中,你可以在任何地方使用这个库,而不需要发布源代码。

最后:

  • PRO: Templates允许模板元编程

  • CON:模板元编程对于新手来说很难理解。

  • CON:模板系统实际上不允许一些在泛型系统中非常简单的类型拓扑

例如,我想象在c++中做这样的事情是很困难的:

class D<T> 
{
    class S { }
    D<D<T>.S> ds;
}
在c#泛型中,

没有问题。在运行时,对于所有引用类型参数,该类型只构建一次。

但是在c++模板中,当你有D<int>时会发生什么?内部类型构造了一个D<D<int>.S>类型的字段,因此我们需要构造该类型。但是该类型构造了一个D<D<D<int>.S>.S>类型的字段…以此类推,直到无穷

Java/c#泛型的限制是什么?

Java泛型是有限的,因为不可能像c++那样做一些技巧。

为了证明这个说法,这里有一个c++的例子,在Java中单独使用模板是不可能复制的。

基于策略的编程是一种在编译时将(模板化)类的使用限制为继承其他(可能的)模板化类的方法。

编译时泛型和运行时泛型的区别是什么?

交易是编译器知道关于类/模板可能的运行时行为的一切,所以它可以做(目前)c#/Java/任何运行时环境/编译器不可能做的大量优化。

这样做的另一个好处是编译器可以确保模板组合的初始化是有效的,这意味着当程序员想要用无效的组合初始化新对象时,不会发生像Java/c#中那样的运行时错误。

c++泛型有什么缺点?

缺点是模板可能变得非常复杂,难以阅读、理解和调试。这可能是Java开发人员不想在语言中有这样一个野兽的原因之一。


Java/c#泛型不可能实现的c++泛型是什么?

在c++中可以使用其他模板作为模板参数,这在c#/Java中是不可能的,并且允许使用优雅的技巧,如模板元编程。

Java泛型的动机始终是提供类型安全,同时保持向后兼容性。Sun通过添加类型检查,然后在编译过程中擦除泛型类型来实现泛型。代码:

// This applies to JDK 1.5, so I won't use <>.
List<Number> list = new ArrayList<Number>();
list.add(2.0);
list.add(-2);
list.add(new BigDecimal("1.23456789");

等价于

List list = new ArrayList();
Double temp = new Double(2.0); // boxing
if (!temp instanceof Number) throw new ClassCastException();
list.add(temp);
// Similar for -2 and the BigDecimal.

不知道list的类型会使它进入运行时类,但是一些instanceof可能会被编译器作为安全而删除。

由于编译器不会将泛型类型写入编译后的类文件中,因此在上面的list.getClass() == ArrayList.class中,不存在像c++中那样的模板特化。List<Boolean>不能被打包成一个比特序列。所有泛型类型都是类型,不像c++中的模板,比如:

template<int length, int time, int mass>
class measurement {...}

可用于量纲分析,并防止人们在区域中添加长度。

根据MSDN, c#泛型和c++模板之间的主要区别是:

  • c#泛型不提供与c++模板相同的灵活性。例如,在c#泛型类中调用算术运算符是不可能的,尽管可以调用用户定义的运算符。
  • c#不允许非类型模板参数,比如模板C{}。c#不支持显式特化;也就是说,一个特定类型的模板的自定义实现。
  • c#不支持部分专门化:对类型参数子集的自定义实现。
  • c#不允许将类型参数用作泛型类型的基类。
  • c#不允许类型参数有默认类型。
  • 在c#中,泛型类型参数本身不能是泛型,尽管构造类型可以用作泛型。c++允许模板参数。

然而,在某些情况下,您可以通过使用扩展方法来解决其中的一些问题。

可用于c++泛型,既不能用于c#泛型也不能用于Java泛型:true 模板元编程(在编译时完成)。

#include <iostream>
template<unsigned U>
struct Fac{ enum { value = U * Fac<U-1>::value};};
template<>
struct Fac<0>{ enum { value = 1};};
template<unsigned U>
struct Fib{ enum {value = (Fib<U-1>::value + Fib<U-2>::value)};};
template<>
struct Fib<0>{ enum {value = 0};};
template<>
struct Fib<1>{ enum {value = 1};};
template<unsigned U>
void show(){
    show<U-1>();
    std::cout << "Fib(" << U << ")=" << Fib<U>::value << "'t" << "Fac(" << U << ")=" << Fac<U>::value << std::endl;
}
template<>
void show<0>(){}
int main(int argc, char** argv){
    show<12>();
}
http://ideone.com/Gdf3W

编辑

c++标准没有对类型参数的约束,而c#和Java有。Boost也有类似的功能(Boost Concept Check Library)。但是从c++ 11开始,你现在可以使用<type_traits>来获得类似的东西。