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_AAdapter. 限制类型的答案是arg 可以的,但是隐式转换为 Adapter 不行。有没有办法做到这一点?最终结果是我希望能够调用ff(A)or f(B),并且在这两种情况下我都需要知道 A 或 B 的实际派生类型ff不能只看到对基类的引用)。

在旁边:

目前,我刚刚开始f(A)工作,f(B)实际上调用了一个执行适配器构造的重载,但我还有其他接受 N 个参数的函数,每个参数可以是 A 或 B,所以我需要 2^N 个重载。

出于好奇,这适用于我正在研究的矩阵库。Base_A表示基本矩阵类型,并Base_B表示基本矩阵视图类型。对于将修改矩阵参数的操作,我需要通过非常量引用传递矩阵或通过 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 个副本只是为了创建处理视图和非视图所需的重载,既繁琐又容易出错。事实上,这还不够好,因为我希望 Scale 能够知道 Mview 的完整派生类型,而不仅仅是基类,因为我可能会生成依赖于 Mview 的类型的实例。

编辑 1:将所有 B 类型更改为具有非常量接口函数。这是最初的意图,因此对任何混淆表示歉意。

编辑 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

您需要将基类作为模板吗?你能注入“更多基础”的非模板类吗?如果是,您可以执行以下操作:

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 回答
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 回答
0

前段时间我不得不这样做,我想出的最简单(维护)的解决方案是只有一个类(矩阵)知道它是拥有自己的数据还是他人数据的视图。例如,

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 回答
0

我没有研究过模板化解决方案的细节,但是当您使用它时,您可以检查 boost 预处理器库来帮助您定义模板的 2^N 变体。这在编译时不会很好,但它会比手动创建变体更好。

于 2009-11-17T23:25:59.943 回答