使用隐式转换模拟C ++函数模板实例化

时间:2009-11-17 21:45:07

标签: c++ templates implicit-conversion

我已经问过two questions与我正在尝试做的事情有关(一个已经解决,其中一个我将很快关闭)。我知道C ++模板实例化不允许任何隐式转换(例如参见this comment),但我想模拟它。

假设我有以下框架代码:

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 can be done类型的答案,但隐式转换为适配器不能。有没有办法做到这一点?最终结果是,我希望能够将f称为f(A)f(B),在这两种情况下,我都需要知道{{1}中的A或B的实际派生类型}(f不能只看到对基类的引用。)

除了:

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

对于好奇,这是我正在处理的矩阵库的应用。 f(B)表示基本矩阵类型,Base_A表示基本矩阵视图类型。对于将修改矩阵参数的操作,我需要通过非const引用传递矩阵或通过const-ref传递可修改的矩阵视图。适配器只是一个简单的矩阵到视图适配器。例如,我目前有一个像

这样的功能
Base_B

所有这些函数的2 ^ N个拷贝只是为了创建所需的重载来处理视图和非视图,这很乏味且容易出错。因为这不够好,因为我希望Scale能够知道Mview的完整派生类型,而不仅仅是基类,因为我可能会生成依赖于Mview的类型实例。

编辑1:将所有B类型更改为具有非const接口函数。这是最初的意图,所以对任何混淆道歉。

编辑2:拥有此工作代码,仍然需要2 ^ N次重载,但除非有人建议如何处理,否则我可以忍受它。

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

4 个答案:

答案 0 :(得分: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的特化,以进一步定制行为。

有趣的是,这是我第一次推荐模板模板参数。

答案 1 :(得分: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);
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

我不久前必须这样做,而我提出的最简单(维护)解决方案是只有一个类(Matrix)知道它是否拥有自己的数据,或者是其他人数据的视图。如,

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
}