0

ループ融合などの最適化を行うために、テンプレートの式パターンを Java で模倣できるかどうかを確認しようとしています。

例として、この式テンプレートの例にある C++ クラスを Java クラスに移植します: https://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example

まず、VecExpression<E>ベクトル式を表すテンプレート クラス。テンプレート パラメーターEを使用し、クラスの型をEコンストラクター パラメーターとして受け取ります。次に、クラス型にキャストするプライベート変数thisAsEセットを作成しますthisE

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();
    }
}

2 つ目は、スーパー コンストラクターに渡され、クラスで呼び出されるメソッドとメソッドを実装するクラスVec拡張です。VecExpression<Vec>Vec.classget()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];
    }
}

そして 3 つ目は、VecSum<E1, E2>を拡張VecExpression<VecSum<E1, E2>し、そのget()メソッドを使用して 2 つの の合計を返すテンプレート クラスVecExpression<E>です。型は明示的なパラメーターとして渡されます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();
    }
}

最後に、式テンプレートを使用して、1 回のメモリ パスで 3 つのベクトルを追加できるクラスを生成します。

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、たとえば a をString明示的に)。

これは、コンパイラがテンプレートからクラス定義を作成し、そのクラスをコンパイルする C++ テンプレートとは対照的です。そのクラスは、テンプレート パラメーターの値に固有の最適化を潜在的に使用するなど、他のクラスとしてコンパイルできます。さらに、C++ でのテンプレートの特殊化により、テンプレート パラメーターで再帰の基本ケースを作成できるため、より一般的なテンプレート メタプログラミングが可能になります。

(上記の理由により、Java で類似の意味で「汎用特殊化」を行うことはできません。Java コンパイラは既に汎用パラメーターをスローしているため、「特殊化された」クラスは、そのようなものを定義しようとすると、 「ジェネリック」クラスと同じです。)

最後に、あなたの例に関してはClass、Javaの大文字の「C」は、から派生することを含め、他のクラスと同じであることに注意してくださいObject。これでは、C++ テンプレートと Java ジェネリックの間のコンパイル時と実行時の違いを回避することはできません。

于 2016-11-02T02:05:40.400 に答える