在一个使用模板化表达式和 Curiously Recursive Template Pattern (CRTP) 的复杂库中,我需要一些重载运算符来专门处理基类,但涉及它们的派生类的操作找不到基类专门化。
即:
- 如果为 BaseA< T > + BaseA< T > 定义了运算符,则代码 DerivedA< T > + DerivedA< T > 会发现该运算符没有问题。
- 如果为 BaseB< T > + BaseB< T > 定义了运算符,则 code DerivedB< T > + DerivedB< T > 发现运算符没有问题。
- 但是如果为 BaseB< BaseA< T >> + BaseB< BaseA< T >> 定义了一个运算符,那么 DerivedB< DerivedA< T >> + DerivedB< DerivedA< T >> 未找到该运算符。
如何确保找到专门的嵌套案例的运算符?
我可以这样重述这个问题:
如果我有类(class)(使用 CRTP)
template<typename derived, typename datatype> class BaseA;
template<typename derived, typename datatype> class BaseB;
template<typename datatype> class DerivedA : public BaseA<DerivedA<datatype>,datatype>;
template<typename datatype> class DerivedB : public BaseB<DerivedB<datatype>,datatype>;
我有一个接线员
template<class derived1, class derived2, class datatype>
operator+(const BaseB<derived1,datatype> &bb1,const BaseB<derived2,datatype> &bb2);
很乐意用来解决函数 DerivedB< DerivedA< double >> + DerivedB< DerivedA< double>>,例如
DerivedB<DerivedA<double> > A;
DerivedB<DerivedA<double> > B;
A+B;
但如果我有一个更专业的运算符(operator)来进行相同的操作
template<class bderived1, class aderived1, class datatype, class bderived2, class aderived2>
operator+(const BaseB<bderived1,BaseA<aderived1,datatype> > &bb1,const BaseB<bderived2,BaseA<aderived2,datatype> > &bb2);
同一函数未找到此运算符
DerivedB<DerivedA<double> > A;
DerivedB<DerivedA<double> > B;
A+B;
如何确保找到专门的运算符(operator)来解决这个功能?
我附上了重现问题的简约代码,只有一行 BA1+BA2; 不使用 g++ 编译。
完整代码示例:
//uses templated expressions
//uses CRTP, see
//http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
//http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Curiously_Recurring_Template_Pattern
//g++ problem.cpp -o problem
#include <iostream> //cout, endl
#include <stdlib.h> //EXIT_SUCCESS
using namespace std;
//TypeC
template<class datatype1, class datatype2>
class TypeC{
public:
TypeC(const datatype1 &d1,const datatype2 &d2){
cout << "helloC" << endl;
};
};
//BaseA
template <typename derived, typename datatype>
class BaseA{
};
//DerivedA
template <typename datatype>
class DerivedA : public BaseA<DerivedA<datatype>,datatype> {
};
//operator for BaseA+BaseA
template<class derived1, class derived2, class datatype>
TypeC< BaseA<derived1,datatype>,BaseA<derived2,datatype> >
operator+(const BaseA<derived1,datatype> &ba1,const BaseA<derived2,datatype> &ba2){
return TypeC< BaseA<derived1,datatype>,BaseA<derived2,datatype> > (ba1,ba2);
};
//BaseB
template <typename derived, typename datatype>
class BaseB{
};
//DerivedB
template <typename datatype>
class DerivedB : public BaseB<DerivedB<datatype>,datatype> {
};
/*for reasons outside the scope of this example, operators for BaseB<> op BaseB<> need specialization, cant use the general case:
//operator for BaseB+BaseB
template<class derived1, class derived2, class datatype>
TypeC< BaseB<derived1,datatype>,BaseB<derived2,datatype> >
operator+(const BaseB<derived1,datatype> &bb1,const BaseB<derived2,datatype> &bb2){
return TypeC< BaseB<derived1,datatype>,BaseB<derived2,datatype> > (bb1,bb2);
};
*/
//operator for BaseB<double>+BaseB<double>
template<class derived1, class derived2>
TypeC< BaseB<derived1,double>,BaseB<derived2,double> >
operator+(const BaseB<derived1,double> &bb1,const BaseB<derived2,double> &bb2){
return TypeC< BaseB<derived1,double>,BaseB<derived2,double> > (bb1,bb2);
};
//operator for BaseB<BaseA>+BaseB<BaseA>
template<class derived1, class derived2, class Aderived1, class Aderived2, class datatype>
TypeC< BaseB<derived1,BaseA<Aderived1,datatype> >,BaseB<derived2,BaseA<Aderived2,datatype> > >
operator+(const BaseB<derived1,BaseA<Aderived1,datatype> > &bb1,const BaseB<derived2,BaseA<Aderived2,datatype> > &bb2){
return TypeC< BaseB<derived1,BaseA<Aderived1,datatype> >,BaseB<derived2,BaseA<Aderived2,datatype> > > (bb1,bb2);
};
int main(int argc, char* argv[]){
DerivedA<double> A1;
DerivedA<double> A2;
A1+A2; //knows this DerivedA+DerivedA is equivalent to BaseA+BaseA, hence calls "operator for BaseA+BaseA"
DerivedB<double> B1;
DerivedB<double> B2;
B1+B2; //knows this DerivedB<double>+DerivedB<double> is equivalent to BaseB<double>+BaseB<double>,
//hence calls "operator for BaseB<double>+BaseB<double>"
DerivedB<DerivedA<double> > BA1;
DerivedB<DerivedA<double> > BA2;
BA1+BA2; //g++ error: no match for ‘operator+’ in ‘BA1 + BA2’
//compiler cannot see this DerivedB<DerivedA<double> > + DerivedB<DerivedA<double> > is equivalent to BaseB<BaseA>+BaseB<BaseA>
//I want it to see this op as equivalent to BaseB<derived1,BaseA<Aderived1,datatype> > + BaseB<derived2,BaseA<Aderived2,datatype> >
//How can I make BaseA act as a wildcard for DerivedA and any other classes derived from it, in this nested case?
return EXIT_SUCCESS;
}
最佳答案
这是因为参数类型,DerivedB<DerivedA<double> >
不是 BaseB<bderived1, BaseA<aderived1,datatype> >
的派生类: operator+
的参数为他们的基类的第二个模板参数传递类型 DerivedA<double>
(这是 datatype
)但是 operator+
的函数参数指定 BaseA<aderived1,datatype>
作为第二个模板参数。
因为里面有这么多类型,很复杂,让我们做一个更简单的例子
template<typename T>
struct B { };
template<typename T>
struct D : B<T> { };
struct base { };
struct derived : base { };
现在,让我们看看它们是如何表现的
template<typename T>
void f(B<T> const&);
void g(B<base> const&);
void h(B<derived> const&);
int main() {
D<derived> dd;
f(dd); // works, like your first case
h(dd); // works too (just not deduced).
g(dd); // does *not* work, just like your second case
}
C++ 标准指定在匹配推导的函数参数类型时考虑派生->基础转换。这就是为什么 file << "hello"
工作:运算符是根据 basic_ostream<C,T>
定义的尽管 file
真的可能是basic_fstream
(它的派生类)。这适用于您的第一种情况。但是在你的第二种情况下,你试图推导出一个完全不是传递的参数的基类的参数。
关于c++ - CRTP 和模板化表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3608595/