C ++模板和Java / C#generics之间有什么区别?有什么限制?

我从这里读了一篇有趣的文章/主题/讨论,我得到了以下问题:

  • Java / C#generics有哪些局限性?
  • 使用Java / C#generics不可能使用C ++模板有什么可能?

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

  • 使用C#generics可以实现哪些模式但使用C ++模板却无法实现?
  • C#的真正generics类型和Java的类型擦除generics类型有什么区别?

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

我认为C ++模板和C#generics之间的主要区别在于C ++模板实际上在构造模板时完全重新编译代码 。 C ++方法的优点和缺点很多:

  • PRO:您可以有效地创建约束,例如“类型参数T必须具有加法运算符”; 如果代码包含几个相互添加的Ts,那么如果使用不允许添加的类型参数构造模板,则模板将无法编译。

  • CON:您可能会意外地创建未记录的约束,例如“类型参数T必须具有加法运算符”。

在C#中,你必须说出哪些约束对用户有帮助,但是你只能受限于一小部分可能的约束:接口,基类,值与引用类型和默认构造函数约束,这就是全部。

  • PRO:对于两种不同的结构,语义分析可能完全不同。 如果你想要那个,那就太棒了。

  • CON:对于两种不同的结构,语义分析可以完全不同。 如果你不想那样,这就是等待发生的错误。

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

  • PRO:您只生成所需构造的代码。

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

模板可能导致codegen变大。 在C#中,生成generics类型的IL一次,然后在运行时,抖动会为程序使用的所有类型执行codegen。 这具有较小的性能成本,但由于抖动实际上仅为所有引用类型参数生成一次代码,因此可以稍微减轻它。 因此,如果您有ListList则jitted代码仅生成一次并用于两者。 ListList相比之下两次代码。

  • PRO:当您使用模板库时,您就拥有源代码。

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

在C#中,generics类型是一流类型。 如果将它们粘贴在库中,则可以在任何地方使用该库,而无需提供源代码。

最后:

  • PRO:模板允许模板元编程。

  • CON:新手很难理解模板元编程。

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

例如,我想在C ++中很难做到这样的事情:

 class D { class S { } D.S> ds; } 

在C#generics中,没问题。 在运行时,类型仅为所有引用类型参数构建一次

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

Java / C#generics有哪些局限性?

Javagenerics是有限的,因为不可能像C ++那样做一些技巧。

为了certificate这里的主张是一个C ++示例,在Java中单独使用模板是不可能的。

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

编译时generics与运行时generics有什么关系?

交易是编译器知道关于类/模板的可能的运行时行为的一切,因此它可以使用C#/ Java /任何运行时环境/编译器进行(当前)不可能的大量优化。

另一个好的方面是编译器可以确保模板组合的实例化是有效的,这意味着当程序员想要用新的对象实现新的对象时,不可能像Java / C#那样发生运行时错误。一个无效的组合。

C ++generics的缺点是什么?

缺点是模板的读取,理解和调试都非常复杂。 这可能是Java开发人员不希望在语言中拥有这样一个野兽的原因之一。


使用Java / C#generics不可能使用C ++ Generics有什么可能?

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

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

 // This applies to JDK 1.5, so I won't use <>. List list = new ArrayList(); 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. 

不知道列表的类型使其成为运行时类,但编译器可能会删除某些instanceof ,因为它们是安全的。

由于编译器不会将generics类型写入已编译的类文件中,因此上面的list.getClass() == ArrayList.class ,因此不能像C ++中那样使用模板特化。 List无法打包成一系列位。 所有generics类型都是类型,与C ++中的模板不同,如:

 template class measurement {...} 

它可以用于尺寸分析,并防止人们增加区域长度。

根据MSDN ,C#generics和C ++模板之间的主要区别是:

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

但是,在某些情况下,您可以使用扩展方法解决其中一些问题。

C ++generics可能,C#generics也不是Javagenerics:真正的模板元编程 (在编译时图灵完成)。

 #include  template struct Fac{ enum { value = U * Fac::value};}; template<> struct Fac<0>{ enum { value = 1};}; template struct Fib{ enum {value = (Fib::value + Fib::value)};}; template<> struct Fib<0>{ enum {value = 0};}; template<> struct Fib<1>{ enum {value = 1};}; template void show(){ show(); std::cout << "Fib(" << U << ")=" << Fib::value << "\t" << "Fac(" << U << ")=" << Fac::value << std::endl; } template<> void show<0>(){} int main(int argc, char** argv){ show<12>(); } 

http://ideone.com/Gdf3W

编辑

当C#和Java有C ++标准时,C ++标准没有对类型参数的约束。 Boost有类似的东西( Boost Concept Check Library )。 但是从C ++ 11开始,你现在可以使用来获得类似的东西。