1

私はすでに、自分がやろうとしていることに関連する2つの 質問をしました(1つは解決し、もう1つはまもなく終了します)。C ++テンプレートのインスタンス化では暗黙的な変換が許可されないことは知っていますが(たとえば、このコメントを参照)、それをシミュレートしたいと思います。

次のスケルトンコードがあるとします。

template <class T>
struct Base_A{
    virtual void interface_func() const = 0;
};
template <class T>
struct Derived_A : public Base_A<T>{
    typedef T value_type;
    void interface_func() const{}
};

template <class T>
struct Base_B{
    virtual void interface_func() = 0; // note: non-const
};
template <class T>
struct Derived_B : public Base_B<T>{
    typedef T value_type;
    void interface_func(){}
};

template <class BType>
struct Adapter : public Base_A<typename BType::value_type>{
    BType &ref_B;
    Adapter(BType &inst_B):ref_B(B_inst){}
    void interface_func() const{} // does stuff with ref_B to simulate an A
};

template <class Should_Always_Be_Base_A>
void f(const Should_Always_Be_Base_A &arg){
    // Only Base_A can be passed in by const ref
    // Passing in a Base_B by const ref would not work.
}

Derived_A<int> A;
Derived_B<int> B;
f(A); // passes in A by const ref
f(B); // I want to pass in Adapter<Derived_B<int> >(B)

関数のテンプレートパラメータfを常にBase_Aまたはの派生クラスにしたいAdapter。のタイプを制限するための答えはarg 実行できますが、アダプタへの暗黙の変換は実行できません。これを行う方法はありますか?f最終的な結果は、f(A)またはを呼び出すことができるようにしたいということですf(B)。どちらの場合も、AまたはBの実際の派生型を知る必要がありますff基本クラスへの参照だけを見ることができません)。

脇:

現在、f(A)作業中です。f(B)実際には、アダプターの構築を実行するオーバーロードを呼び出しますが、N個の引数を取り、それぞれがAまたはBになる可能性がある他の関数があるため、2^N個のオーバーロードが必要になります。

不思議なことに、これは私が取り組んでいるマトリックスライブラリに適用されています。Base_A基本マトリックスタイプをBase_B表し、基本マトリックスビュータイプを表します。行列の引数を変更する操作の場合、非const参照によって行列を渡すか、const-refによって変更可能な行列ビューを渡す必要があります。アダプターは、単純なマトリックスからビューへのアダプターです。たとえば、私は現在、次のような機能を持っています

Scale(const MatrixViewBase<T> &Mview, const T &scale_factor){
    // does the actual work
}
Scale(MatrixBase<T> &M, const T &scale_factor){
    Scale(Adapter<MatrixBase<T> >(M), scale_factor);
}

ビューと非ビューの両方を処理するために必要なオーバーロードを作成するためだけに、これらすべての関数の2^Nコピーを作成するのは面倒でエラーが発生しやすくなります。現状では、Mviewに依存する型のインスタンスを生成する可能性があるため、Scaleが基本クラスだけでなくMviewの完全な派生型を認識できるようにしたいので、これでは十分ではありません。

編集1:すべてのBタイプを非constインターフェース機能を持つように変更しました。これが本来の意図でしたので、ご迷惑をおかけしましたことをお詫び申し上げます。

編集2:この動作するコードを持っていて、それでも2 ^ Nのオーバーロードが必要ですが、誰かがそれに対処する方法を提案しない限り、私はそれで生きることができます。

#include <iostream>

template <class T>
struct ReadableMatrix{
    typedef T value_type;
};
template <class T>
struct WritableMatrix{
    typedef T value_type;
};
template <class T>
struct WritableMatrixView{
    typedef T value_type;
};

template <class T>
struct Matrix : public WritableMatrix<T>{
    typedef T value_type;
    typedef ReadableMatrix<T> readable_matrix;
    typedef WritableMatrix<T> writable_matrix;
};

template <class T>
struct MatrixView : public WritableMatrixView<T>{
    typedef T value_type;
    typedef ReadableMatrix<T> readable_matrix; // not really used; needs an adapter before using
    typedef WritableMatrixView<T> writable_matrix;
};

template <class T, class R>
struct IsReadableMatrix{
};
template <class T, class R>
struct IsReadableMatrix<ReadableMatrix<T>, R>{
    typedef R type;
};

template <class T, class R>
struct IsWritableMatrix{
};
template <class T, class R>
struct IsWritableMatrix<WritableMatrix<T>, R>{
    typedef R type;
};

template <class T, class R>
struct IsWritableMatrixView{
};
template <class T, class R>
struct IsWritableMatrixView<WritableMatrixView<T>, R>{
    typedef R type;
};

template <class TA, class TB>
    typename IsReadableMatrix<typename TA::readable_matrix,
    typename IsWritableMatrixView<typename TB::writable_matrix,
void
    >::type>::type
Copy(const TA &A, const TB &B){
    std::cout << "Here" << std::endl;
}

template <class TA, class TB>
    typename IsReadableMatrix<typename TA::readable_matrix,
    typename IsWritableMatrix<typename TB::writable_matrix,
void
    >::type>::type
Copy(const TA &A, TB &B){
    std::cout << "Here2" << std::endl;
}

int main(){
    Matrix<int> M, M2;
    MatrixView<int> V, V2;
    Copy(M, M2);
    Copy(V, V2);
    Copy(M, V);
    Copy(V, M);
}
4

4 に答える 4

1

次のことを試してください。

template <template <typename> View, typename T>
struct Adapter
{
    // Leave empty to cause errors if used, or you could
    // provide a generic adapter for View<typename T::value_type>
}

// Partial specialization for a given base.
template <typename T>
struct Adapter<MatrixView, T> : MatrixView<typename T::value_type>
{
    const T& t;

    Adapter (const T& t)
      : t(t)
    {}

    void some_virtual()
    {
      // Do stuff
    }
}

template <template <typename> View, typename T>
const View<T>& adapt (const View<T>& v)
{
  return v;
}

template <template <typename> View, typename T>
View<T> adapt (const T& t)
{
  return Adapter<View, T>(t);
}

template <typename T, typename U>
foo(const MatrixViewBase<T> &Mview, const MatrixViewBase<U> &Mview2);

template <typename T, typename U>
foo (const T& t, const U& u)
{
  return foo(adapt<MatrixViewBase>(t), adapt<MatrixViewBase>(u));
}

const私はできる限りどこにでも入れました。適切でない場合は、すべての状況で使用する必要はありません。の特殊化を使用して、動作をさらに調整できAdapterますT

興味深いことに、テンプレートテンプレートパラメータを推奨するのはこれが初めてです。

于 2009-11-17T22:46:08.827 に答える
1

基本クラスをテンプレートにする必要がありますか? 「より基本的な」非テンプレートクラスでも注入できますか? はいの場合、次のようなことができます。

struct Base_A{
    virtual void interface_func() const = 0;
};

template <class T>
struct Derived_A : public Base_A{
    typedef T value_type;
    void interface_func() const{}
};

struct Base_B{
    virtual void interface_func() const = 0;
};

template <class T>
struct Derived_B : public Base_B{
    typedef T value_type;
    void interface_func() const{}
};

struct Adapter
{
    const Base_A* pA;
    const Base_B* pB;

    Adapter(const Base_B &inst_B) : pB(&inst_B), pA(0){}
    Adapter(const Base_A &inst_A) : pA(&inst_A), pB(0){}
    void interface_func() const
    {
        if( 0 != pA )
            pA->interface_func();
        else if( 0 != pB )
            pB->interface_func();
    }
};


void f(const Adapter &arg)
{
    arg.interface_func(); // will call proper interface_func
}


int main()
{
    Derived_A<int> A;
    Derived_B<int> B;
    f(A);
    f(B);
}
于 2009-11-17T22:47:54.987 に答える
0

テンプレート化されたソリューションの詳細については調べていませんが、その間に、ブーストプリプロセッサライブラリをチェックして、テンプレートの2^Nバリエーションを定義するのに役立てることができます。これはコンパイル時にはうまくいきませんが、バリエーションを手動で作成するよりはましです。

于 2009-11-17T23:25:59.943 に答える
0

私は少し前にこれをしなければなりませんでしたが、私が思いついた最も簡単な(維持する)解決策は、それが独自のデータを所有しているか、他の誰かのデータのビューであるかを知っているクラス(Matrix)を1つだけ持つことでした。例えば、

Matrix A(n,m);  //Creates a new matrix of size (n,m)
Matrix B=View(A);  //Creates a matrix that's a view into A
Matrix C=View(pData,n,m); //Creates a matrix thats a view of data in a pointer that someone else owns

Foo(A);
Foo(B);
Foo(C);

Matrix クラスの記述が少し難しくなりますが、後で Matrix を使用する人は、それをマトリックスとして使用するだけで、それがビューであるかどうかを気にする必要はありません。

追加するために編集:

BLAS に移調を任せてみませんか?結局、どの BLAS 関数も、連続したメモリ チャンクへのポインタを必要とするだけです。Matrix クラスがそのメモリに到達する方法を知っている限り、問題はありません。Matrix クラスに独自の isTransposed フラグが内部にある場合、次のようなことができます。

Matrix A(n,m);
Matrix B=TransposedView(pData,m,n);
Matrix C=A*B;

Matrix& operator*(const Matrix& lhs, const Matrix& rhs)
{
  Matrix result(lhs.Rows(),rhs.Cols()); //or did i get that backwards :)
  char TransL=lhs.IsTransposed()?'T':'N';
  char TransR=rhs.IsTransposed()?'T':'N';
  _dgemm(TransL, TransR, ..., lhs.Data(), ..., rhs.Data(), ..., result.Data());
  return result
}
于 2009-11-17T23:42:38.223 に答える