0

テンプレート式とCuriouslyRecursiveTemplate Pattern(CRTP)を使用する複雑なライブラリでは、オーバーロードされた演算子を基本クラスに特化する必要がありますが、派生クラスを含む操作では基本クラスの特殊化が見つかりません。

すなわち:

  • BaseA <T> + BaseA <T>に対して演算子が定義されている場合、コードDerivedA <T> +DerivedA<T>は演算子に問題がないことを検出します。
  • BaseB <T> + BaseB <T>に対して演算子が定義されている場合、コードDerivedB <T> +DerivedB<T>は演算子に問題がないことを検出します。
  • ただし、BaseB <BaseA <T>> + BaseB <BaseA <T>>に対して演算子が定義されている場合、DerivedB <DerivedA <T>> + DerivedB<DerivedA<T>>はその演算子を検出しません。

特殊なネストされたケースの演算子が確実に見つかるようにするにはどうすればよいですか?

このように問題を言い換えることができます:

クラスがある場合(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;

しかし、代わりに同じ操作のためのより専門的なオペレーターがいる場合

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;

この関数を解くための専門のオペレーターを確実に見つけるにはどうすればよいですか?

問題を再現するために、1行の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

1 に答える 1

3

これは、引数の型DerivedB<DerivedA<double> >が の派生クラスではないためですBaseB<bderived1, BaseA<aderived1,datatype> >: のoperator+基本クラスの 2 番目のテンプレート引数の引数は、型DerivedA<double>( datatype) を渡しましたが、operator+関数パラメーターBaseA<aderived1,datatype>は 2 番目のテンプレート引数として指定されています。

これらの多くのタイプがあると非常に複雑になるため、より単純な例を作成しましょう

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>ています。これは最初のケースに適用されます。しかし、2番目のケースでは、渡された引数の基本クラスではないパラメーターを推測しようとしています。filebasic_fstream

于 2010-09-03T16:49:51.553 に答える