【问题标题】:CRTP and templated expressionsCRTP 和模板化表达式
【发布时间】:2010-08-31 11:36:06
【问题描述】:

在使用模板化表达式和好奇递归模板模式 (CRTP) 的复杂库中,我需要一些重载运算符来专门用于基类,但涉及其派生类的操作找不到基类专门化。

即:

  • 如果为BaseA+BaseA定义了算子,那么代码DerivedA+DerivedA发现算子没有问题。
  • 如果为 BaseB + BaseB 定义了运算符,则 code DerivedB + DerivedB 发现算子没问题。
  • 但如果为 BaseB > + BaseB > 定义了运算符,则 DerivedB > + DerivedB > 未找到该运算符。

如何确保找到专门的嵌套案例的运算符?

我可以重述这个问题:

如果我有课程(使用 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 > + DerivedB >,例如

DerivedB<DerivedA<double> > A;
DerivedB<DerivedA<double> > B;
A+B;

但如果我有一个更专业的运算符来代替相同的操作

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;

如何确保找到专门的算子来解决这个功能?

我附上了重现问题的简化代码,只有一行 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;

}

【问题讨论】:

  • 这个条款有问题吗?如果参数类型不包含参与模板参数推导的模板参数,则将对函数参数执行隐式转换(第 4 条)以将其转换为相应函数参数的类型。
  • 我不认为我理解该条款,因为我是自学成才的,而且不完全如此。在我看来,使用模板化基类的运算符重载对于使用派生类操作数的操作来说效果很好,只要模板化操作数没有嵌套。这很完美。但是我想让它适用于嵌套在另一个派生类中的派生类,我看不出如何帮助编译器将嵌套的派生类操作数识别为适合嵌套的基类运算符重载?
  • @finlaylabs:我对模板还是很基础的。我认为我们需要破解为什么 C++ BaseB, DerivedA> &r = BA1; 中不允许使用 'r2' BaseB >, BaseA, double> > &r2 = BA1;
  • 感谢 chubsdad 的 cmets,我也无法理解为什么不允许使用 'r2'。如果不允许,如何克服?
  • @finlaylabs:这是因为给定类 'A' 和它的派生类 'B',而 'X' 是一个函数模板,有些 'T',X 是不一样的作为 X。即使“A”是“B”的基础,也不会发生将“B”转换为“A”的隐式转换

标签: c++ templates expression crtp


【解决方案1】:

这是因为参数类型DerivedB&lt;DerivedA&lt;double&gt; &gt; 不是BaseB&lt;bderived1, BaseA&lt;aderived1,datatype&gt; &gt; 的派生类:operator+ 的参数为其基类的第二个模板参数传递了DerivedA&lt;double&gt; 类型(即datatype)但是operator+ 的函数参数将BaseA&lt;aderived1,datatype&gt; 指定为第二个模板参数。

因为里面有这么多类型,所以很复杂,让我们做一个更简单的例子

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 &lt;&lt; "hello" 起作用的原因:运算符是根据basic_ostream&lt;C,T&gt; 定义的,即使file 真的可以是basic_fstream(它的派生类)。这适用于您的第一种情况。但是在第二种情况下,您尝试推断出一个完全不是所传递参数的基类的参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-15
    • 1970-01-01
    相关资源
    最近更新 更多