0

我正在尝试查看是否可以在 Java 中模仿模板表达式模式,以进行循环融合等优化。

例如,我将此表达式模板示例中的 c++ 类移植到 java 类:https ://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example

VecExpression<E>首先,一个表示向量表达式的模板类。它使用模板参数E并将类类型E作为构造函数参数。然后它创建一个私有变量thisAsE集以this转换为E

public abstract class VecExpression <E> {
    private VecExpression thisAsE;

    public VecExpression(Class<E> type) throws Exception {
        if(type.isInstance(this)) {
            thisAsE = (VecExpression)type.cast(this);   
        }
        else {
            throw new Exception("Class type must extend VecExpression");
        }
    }

    public double get(int i) {
        return thisAsE.get(i);
    }

    public int size() {
        return thisAsE.size();
    }
}

其次,一个类Vec扩展VecExpression<Vec>,它传递Vec.class给超级构造函数并实现类中调用的get()和方法。size()VecExpression<E>

public class Vec extends VecExpression<Vec> {

    private double[] elems;

    public <E> Vec(VecExpression<E> expression) throws Exception {
        super(Vec.class);
        for(int i = 0; i < expression.size(); ++i) {
            elems[i] = expression.get(i);
        }
    }

    public Vec(double[] elems) throws Exception {
        super(Vec.class);
        this.elems = elems;
    }

    public double get(int i) {
        return elems[i];
    }
}

VecSum<E1, E2>第三,扩展的模板类VecExpression<VecSum<E1, E2>,并使用其get()方法返回两个VecExpression<E>s 的和。类型作为显式参数传递Class<VecSum<E1, E2>> type

public class VecSum <E1, E2> extends VecExpression<VecSum<E1, E2>> {

    private VecExpression u;
    private VecExpression v;

    public VecSum(Class<VecSum<E1, E2>> type, VecExpression<E1> u, VecExpression<E2> v) throws Exception {
        super(type);
        if(u.size() != v.size()) {
            throw new Exception("Vectors must be of the same size");
        }
        this.u = u;
        this.v = v;
    }

    public double get(int i) {
        return u.get(i) + v.get(i);
    }

    public int size() {
        return v.size();
    }
}

最后,我们使用表达式模板生成一个类,该类可以一次通过内存添加三个向量。

public class Main {
    public static void main(String[] args) throws Exception {
        Vec a = new Vec(new double[] {1, 2, 3});
        Vec b = new Vec(new double[] {1, 2, 3});
        Vec c = new Vec(new double[] {1, 2, 3});

        VecSum<Vec, Vec> ab = new VecSum<Vec, Vec>(VecSum<Vec, Vec>.class, a, b);
        VecSum<VecSum<Vec, Vec>, Vec> abc = new VecSum<>(VecSum<VecSum<Vec, Vec>, Vec>.class, ab, c);
    }
}

根据 Louis Wasserman 的评论编辑

但是,传递给VecSum构造函数的类类型不起作用,因为表达式试图从参数化类型中获取类。Louis 指出,泛型类的实现不会像在 c++ 中那样编译成不同的类。您将如何传递它们的类型,或者是否有另一种表达模板模式的方法?

4

1 回答 1

2

您尝试做的事情在 Java 中不起作用,至少在您试图通过使用 Java 泛型来获得编译时优化的情况下。原因是,与 C++ 模板不同,Java 泛型不会在编译时得到解析。由于编译器没有在编译时解析类型,它不能使用任何关于它的东西来进行编译时优化。在某种意义上,Java 编译器创建的字节码完全“擦除”了通用信息。如果您的 Java 类在代码中出现的class C<A>任何地方都存在该类型A,则将其替换为 class Object。如果您的 Java 类在您的代码class D<E extends F>中出现的所有位置,E则将替换为F.

在这种情况下,您可能会问为什么要使用泛型。答案是,在编译器丢弃参数之前,它确实对输入进行类型安全检查,并在方法返回时隐式插入强制转换。这是在几个版本之前添加到 Java 中的一种便利,但是 Java 容器类ArrayList已经存在。只是因为输入是明确的Object(让您放入任何对象,即使您知道它应该只包含String对象并强制您强制转换to的结果get,比如说,aString明确的)。

这与编译器从模板创建类定义并编译该类的 C++ 模板形成对比。然后可以将该类编译为任何其他类,包括可能使用特定于模板参数值的优化。此外,C++ 中的模板特化允许更普遍地进行模板元编程,因为它允许您为模板参数中的递归创建基本情况。

(由于上述原因,您不能在 Java 中具有任何类似意义上的“通用专业化” - Java 编译器已经抛出了通用参数,因此您的“专业化”类 - 如果您尝试定义这样的东西 - 将是与“通用”类相同。)

最后,关于您的示例,请记住,ClassJava 中带有大写“C”的类与任何其他类一样,包括它派生自Object. 这不会让您了解 C++ 模板和 Java 泛型之间的编译时与运行时差异。

于 2016-11-02T02:05:40.400 回答