【问题标题】:What are the Differences between C++ Templates and Java/C# Generics and what are the limits? [closed]C++ 模板和 Java/C# 泛型有什么区别,有什么限制? [关闭]
【发布时间】:2013-04-07 00:30:56
【问题描述】:

我从here 阅读了一篇有趣的文章/线程/讨论,我收到了以下问题:

  • Java/C# 泛型有哪些限制?
  • C++ 模板可以实现哪些 Java/C# 泛型无法实现的功能?

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

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

【问题讨论】:

  • 您可能会问的第三个有趣的问题是“C# 泛型可能有哪些模式,而 C++ 模板不可能?”您可能会问的第四个问题是“C# 的真正泛型类型和 Java 的类型擦除泛型类型有什么区别?”
  • 我认为在这里不要混淆命名是有帮助的,C# 和 Java 没有模板,标准 C++ 没有泛型。
  • 这是我的菜鸟错,好点
  • 只是好奇为什么没有将其移至程序员。这绝对是一个有效的问题。但我也认为这是一个有效的问题。他要求比较/对比 C++ 模板与 C# 泛型与 Java 泛型。这不是要求哪个更好,而是要求对差异进行基于事实的评估。

标签: c# java c++ generics template-meta-programming


【解决方案1】:

首先,您可能想阅读my 2009 article on this subject。

在我看来,C++ 模板和 C# 泛型之间的主要区别在于,C++ 模板实际上在构建模板时完全重新编译代码。 C++ 方法的优缺点很多:

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

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

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

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

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

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

  • 专业人士:您只需为您需要的结构生成代码。

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

模板会导致代码生成变大。在 C# 中,泛型类型的 IL 生成一次,然后在运行时抖动对程序使用的所有类型进行代码生成。这会带来很小的性能成本,但由于抖动实际上只为所有引用类型参数生成一次代码,这在一定程度上有所缓解。因此,如果您有List<object> 和List<string>,那么jitted 代码只会生成一次并用于两者。相比之下,List<int> 和 List<short> 会 jit 两次代码。

  • 专业人士:当您使用模板库时,源代码就在那里。

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

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

最后:

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

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

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

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

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

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

但是在 C++ 模板中,当你有 D&lt;int&gt; 时会发生什么?内部类型构造了一个D&lt;D&lt;int&gt;.S&gt; 类型的字段,所以我们需要构造那个类型。但该类型构造了一个 D&lt;D&lt;D&lt;int&gt;.S&gt;.S&gt;... 类型的字段,以此类推直到无穷大。

【讨论】:

  • 您可能想明确指出,您的所有“专业”项目都是“优势 C++”,即使前一段谈到 C#。值得一提的另一件事是,C# 期望在一个框架上运行,其中所有类对象实例都有一个运行时类型,该类型独立于对其引用的编译时类型,并且可以在以下位置创建类型描述符运行。在 .net 中编写代码是可能的——没有反射——将采用任意字符串并生成具有将返回该字符串的静态成员的泛型类的实例。
  • 更好的是,例程可以接受实现接口的通用对象,该接口包括具有通用类型参数的方法,并且可以使用适当类型的实例调用该方法。设想事情的最简单方法是如果字符串被限制为字符 AZ,给定参数"ERIC"代码可能有效地调用PassedInObject.Invoke(new Thing&lt;E&lt;R&lt;I&lt;C&lt;Root&gt;&gt;&gt;&gt;()); 这种代码可以生成的类的数量实际上没有限制,但是运行- time 只需要生成实际使用的那些。
【解决方案2】:

Java/C# 泛型有哪些限制?

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

为了证明here 是一个 C++ 示例,仅用 Java 中的模板是不可能重现的。

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

编译时泛型与运行时泛型有何不同?

交易是编译器了解类/模板可能的运行时行为的所有信息,因此它可以进行大量优化,而 C#/Java/任何运行时环境/编译器(目前)是不可能的。

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

C++ 泛型的缺点是什么?

缺点是模板的阅读、理解和调试会变得非常复杂。这(也许)是 Java 开发人员不想在语言中拥有这样一个野兽的原因之一。


C++ 泛型可以实现哪些 Java/C# 泛型无法实现?

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

【讨论】:

  • 至少基于策略的设计的链接示例可以与 C# 泛型一起使用。您只需要添加约束(如where TOutputPolicy : IOutputPolicy),以便已知使用的方法存在。但总的来说,模板更强大是对的:它们在编译时使用枚举/结构组合作为基本数据类型具有图灵完整性。 (您可能需要增加编译器模板深度)。
  • C++ 模板是否真的有一些东西让它们比泛型更难理解?还是只是它们在 STL 中的使用方式?
  • 使用 C++ 模板,您可以调用 TemplateParameter 可能没有的方法(好吧,您不能 - 您会遇到严重的编译器错误,但至少我使用的 IDE 在编译前没有告诉我)。在 C# 中,这需要接口约束,因此您的模板类只能使用在该接口中定义的方法/属性。见msdn
【解决方案3】:

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.

不知道列表的类型使其进入运行时类,但编译器可能会删除一些instanceofs,因为它是安全的。

由于编译器不会将泛型类型写入已编译的类文件中,因此上述list.getClass() == ArrayList.class,因此不能像 C++ 中那样存在模板特化。 List&lt;Boolean&gt; 不能打包成位序列。所有泛型类型都是类型,不像 C++ 中的模板:

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

可用于维度分析,并防止人们增加区域的长度。

【讨论】:

    【解决方案4】:

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

    • C# 泛型不提供与 C++ 模板相同的灵活性。例如,虽然可以调用用户定义的运算符,但不能在 C# 泛型类中调用算术运算符。
    • C# 不允许非类型模板参数,例如模板 C {}。
    • C# 不支持显式特化;也就是说,针对特定类型的模板的自定义实现。
    • C# 不支持部分特化:类型参数子集的自定义实现。
    • C# 不允许将类型参数用作泛型类型的基类。
    • C# 不允许类型参数具有默认类型。
    • 在 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>();
    }
    

    http://ideone.com/Gdf3W

    编辑

    C++ 标准对类型参数没有限制,而 C# 和 Java 有。 Boost 有类似的东西 (Boost Concept Check Library)。但是从 C++11 开始,您现在可以使用 &lt;type_traits&gt; 来获得类似的东西。

    【讨论】:

      猜你喜欢
      • 2010-09-07
      • 1970-01-01
      • 2016-12-05
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多