【发布时间】:2021-02-10 14:33:32
【问题描述】:
Java 泛型和 C++ 模板都有其优点和缺点。 Java 泛型生成的代码更少,有助于最小化可执行文件的大小,而 C++ 为模板的每个不同实例重写整个模板代码。另一方面,C++ 模板支持基本数据类型甚至常量类型作为参数实参,而 Java 只支持类作为泛型的参数。
为什么 C++ 不能两者兼得?当用户想要在函数中使用类型时,他可以使用 C++ 样式模板,否则他可以在声明参数类/函数时使用 Java 样式泛型。什么技术性阻碍了这个想法的实施?
编辑:例如考虑一下:
struct A {
virtual void do_something() {}
};
struct B: public A {
virtual void do_something() {}
};
template <typename T>
void call_do_something(T arg) {
arg.do_something();
}
int main() {
A a;
call_do_something(a);
B b;
call_do_something(b);
}
class A {
public void do_something() {}
}
class B extends A {
public void do_something() {}
}
class Z {
public static void <T extends A> call_do_something(T arg) {
arg.do_something();
}
}
public class Main {
public static void main(String[] args) {
Z.call_do_something(new A());
Z.call_do_something(new B());
}
}
据我所知,在编译时,上面的 C++ 代码会生成两个版本的call_do_something,一个用于 A,一个用于 B。但下面的 java 中的等效代码只会生成一个 call_do_something。这将减少生成的可执行文件大小。
我并不是说 Java 泛型优于 C++ 模板。我只是想知道为什么这不是在 C++ 中实现的,是否有技术原因,是否在过去提出过等等。抱歉造成混淆。
编辑:当我不深入了解这些概念时,有人问了这个问题。现在我认为我对 C++ 中的模板和 Java 中的泛型有了更好的理解,我正在尝试自己回答这个问题。
Java 泛型可以通过使用类型擦除而受益,因为它具有单根类层次结构,并且对象总是通过引用传递(请注意,通过值传递的类型,如 int 和 float 不能在泛型中使用) . Java 编译器会擦除类型参数的实际泛型类型,并将其替换为参数的上限。 Java 的泛型与 C++ 中的运行时多态性更相关。
另一方面,C++ 模板比 Java 泛型可以做的更多。它可以按值调用,可以用两个不相关的类实例化模板等。但它不能实现类型擦除,因为对象可以按值传递,完成后,它们的大小和子结构会发生变化,单个机器代码无法处理它们全部。
但在特定情况下,如果您有一个公共基类,并且您通过引用传递对象,那么您不应该使用 C++ 中的模板。您应该改用虚函数的运行时多态性。
以下Java中的泛型代码实际上等价于:
class AList<E extends A> {
private List<E> list = new ArrayList<E>();
public E add(E ele) {
list.add(ele);
return ele;
}
}
public class Main {
public static void main(String[] args) {
AList<B> l = new AList();
B b = l.add(new B());
}
}
Java 中的这个:
class AList {
private List<A> list = new ArrayList<A>();
public void add(A ele) {
list.add(ele);
}
}
public class Main {
public static void main(String[] args) {
AList l = new AList();
B b = (B)l.add(new B());
}
}
这在 C++ 中:
class AList {
private:
std::vector<A&> vec = {};
public:
A& add(A& ele) {
vec.push_back(ele);
return ele;
}
}
int main() {
AList l;
B btemp;
B& b = static_cast<B&>(l.add(&btemp));
}
TL;DR 类型擦除在 Java 泛型中是有效的,因为它具有单根类层次结构并且对象总是通过引用传递。由于这些在 C++ 中不是这样,类型擦除不适合 C++ 模板。
我想把这个放在答案中。但是由于问题已关闭,并且我尝试重新打开失败,因此我将其添加到此处。
【问题讨论】:
-
C++ 有类型擦除。
-
Java's type erasure has clear advantages[需要引用]。 -
@MikeVine Java 的类型擦除具有明显的优势 - eerorika on stackoverflow。平心而论,它也有明显的缺点。
-
Mainly Reduced Generated Code。这有一些警告。如果模板的 2 个实例化的代码会生成完全相同的字节码,那么编译器可能会共享它们(参见 opt:ICF)。所以 C++ 和 Java 一样好。如果实例化的代码不同,那么根据定义,如果您共享实例化,您将为其中之一生成效率较低的代码。所以在这种情况下,C++ 比 Java 更好(更快)。所以 C++ >= Java(稍微开玩笑) -
函数应该写成
void call_do_something(A& arg)。不需要模板。需要一个更好的例子。
标签: c++ templates generics type-erasure