我从here读了一篇有趣的文章/话题/讨论,我得到了以下问题:
修改1 Eric Lippert
提出的更多推荐问题答案 0 :(得分:9)
首先,您可能需要阅读my 2009 article on this subject。
C ++模板和C#泛型之间的主要区别在于,C ++模板实际上在构建模板时完全重新编译代码。 C ++方法的优点和缺点很多:
PRO:您可以有效地创建约束,例如“类型参数T必须有一个加法运算符”;如果代码中包含几个相互添加的Ts,那么如果使用不允许添加的类型参数构造模板,模板将无法编译。
CON:您可能会意外地创建未记录的约束,例如“类型参数T必须具有加法运算符”。
在C#中你必须说哪些约束对用户有帮助,但是你只限于一小部分可能的约束:接口,基类,值与引用类型和默认构造函数约束,就是这样。
PRO:对于两种不同的结构,语义分析可能完全不同。如果你想要,那就太棒了。
CON:对于两种不同的结构,语义分析可能完全不同。如果你不想那样,这就是等待发生的错误。
在C#中,无论构造类型的次数是多少次,语义分析都是完成,因此需要使用符合约束条件的任何类型参数,而不仅仅是实际提供的类型参数。
PRO:您只生成完全符合您需要的构造的代码。
CON:您为所使用的所有构造生成代码。
模板可能会导致codegen变大。在C#中,一般类型的IL生成一次,然后在运行时抖动为程序使用的所有类型执行codegen。这具有较小的性能成本,但由于抖动实际上仅为所有引用类型参数生成一次代码,因此可以稍微减轻这种影响。因此,如果您有List<object>
和List<string>
,则jitted代码仅生成一次并用于两者。相比之下,List<int>
和List<short>
会两次使用代码。
PRO:当您使用模板库时,您就拥有源代码。
CON:要使用模板库,您必须拥有源代码。
在C#中,泛型类型是一流类型。如果将它们粘贴在库中,则可以在任何地方使用该库,而无需发送源代码。
最后:
PRO:模板允许模板元编程。
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>
类型的字段......依此类推到无穷大。
答案 1 :(得分:3)
Java泛型是有限的,因为不可能像C ++那样做一些技巧。
证明声明here是一个C ++示例,在Java中单独使用模板无法重现。
基于策略的编程是一种在编译时将(模板化)类的使用限制为继承的其他(可能的)模板类的方法。
编译时泛型与运行时泛型有什么关系?
协议是编译器知道关于类/模板的可能运行时行为的所有内容,因此它可以使用C#/ Java /任何运行时环境/编译器进行(目前)不可能的大量优化。
另一个好处是编译器可以确保模板组合的实例化是有效的,这意味着当程序员想要实例化时,不可能像Java / C#中那样发生运行时错误。具有无效组合的新对象。
C ++泛型的缺点是什么?
缺点是模板读取,理解和调试变得非常复杂。这可能是Java开发人员不希望在某种语言中拥有这样一种野兽的原因之一。
在C ++中可以使用其他模板作为模板参数,这在C#/ Java中是不可能的,并且允许使用模板元编程等优雅技巧。
答案 2 :(得分:3)
Java Generics的动机始终是提供类型安全性,同时保持向后兼容性。 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.
不知道列表的类型使其成为运行时类,但编译器可能会删除一些instanceof
作为安全。
由于编译器没有将泛型类型写入已编译的类文件中,因此上面的list.getClass() == ArrayList.class
中没有像C ++中那样的模板特化。 List<Boolean>
无法打包成一系列位。所有泛型类型都是类型,与C ++中的模板不同,如:
template<int length, int time, int mass>
class measurement {...}
可用于尺寸分析,并防止人们增加区域长度。
答案 3 :(得分:1)
根据MSDN,C#泛型和C ++模板之间的主要区别是:
然而,在某些情况下,您可以使用扩展方法解决其中一些问题。
可能使用C ++泛型,无论是C#泛型还是Java泛型:真正的模板元编程(在编译时图灵完成)。
#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>();
}
修改强>
C ++标准在C#和Java时没有对类型参数的限制。 Boost有类似的东西(Boost Concept Check Library)。但是从C ++ 11开始,你现在可以使用<type_traits>
来获得类似的东西。