我已经问过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_A
或Adapter
的派生类。限制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);
}
答案 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
}