我如何将 CRTP 中的模板派生 class 与派生的 class 表达式模板结合起来?
How can I combine templated derived class in CRTP with derived class expression templates?
我的目标是实现一个向量 class Vec
,它允许像 auto vecRes = vecA + vecB * vecC
这样的算术表达式的高效计算。这是一个已知问题,可以在维基百科上找到使用 Curiously Recurring Template Pattern (CRTP) 的解决方案。
我首先采用硬编码向量元素类型 double
的实现,包括用于添加的派生 class,
template <typename Exn>
class VecExn
{
public:
double operator[] ( int32_t idx ) const { return static_cast<Exn const&>( *this )[idx]; }
int32_t size() const { return static_cast<Exn const&>( *this ).size(); }
};
class Vec: public VecExn<Vec>
{
public:
Vec() {}
Vec( std::initializer_list<double> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) : eles_( exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
double operator[] ( int32_t idx ) const { return eles_[idx]; }
double& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<double> eles_;
};
template <typename Lhs, typename Rhs>
class VecSum: public VecExn<VecSum<Lhs, Rhs> >
{
public:
VecSum( Lhs const& lhs, Rhs const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
double operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs const& lhs_;
Rhs const& rhs_;
};
正常工作。
但是,在我用模板替换 double
的方法中,基础 class 获得了模板模板参数,而普通 Vec
当然变成了模板化。
template < typename Typ, template<typename> typename Exn >
class VecExn
{
public:
Typ operator[] ( int32_t idx ) const { return static_cast<Exn<Typ> const&>( *this )[idx];}
int32_t size() const { return static_cast<Exn<Typ> const&>( *this ).size(); }
};
template <typename Typ>
class Vec: public VecExn<Typ, Vec >
{
public:
Vec() {}
Vec( std::initializer_list<Typ> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) :eles_(exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
Typ operator[] ( int32_t idx ) const { return eles_[idx]; }
Typ& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<Typ> eles_;
};
问题出现在 VecSum
class 定义中,该定义在硬编码 double
案例中已经是模板 class,现在不被识别为VecExn
.
的正确形式
template <typename Typ, template <typename> typename Lhs, template <typename> typename Rhs>
class VecSum: public VecExn<Typ, VecSum<Typ, Lhs, Rhs> > // ERROR: does not match the template parameter list for template parameter 'Exn'
{
public:
VecSum( Lhs<Typ> const& lhs, Rhs<Typ> const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
Typ operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs<Typ> const& lhs_;
Rhs<Typ> const& rhs_;
};
我该如何解决这个问题?
正如@super 和@Jarod42 指出的那样,解决方案非常简单:
不要在基础 class 或运算符的表达式模板中使用模板模板参数,而是替换 return 类型的 double operator[]
来自 auto
.
template <typename Exn>
class VecExn
{
public:
auto operator[] ( int32_t idx ) const { return static_cast<Exn const&>( *this )[idx]; }
int32_t size() const { return static_cast<Exn const&>( *this ).size(); }
};
template <typename Typ>
class Vec: public VecExn<Vec<Typ>>
{
public:
Vec() {}
Vec( std::initializer_list<Typ> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) : eles_( exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
Typ operator[] ( int32_t idx ) const { return eles_[idx]; }
Typ& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<Typ> eles_;
};
template <typename Lhs, typename Rhs>
class VecSum: public VecExn<VecSum<Lhs, Rhs> >
{
public:
VecSum( Lhs const& lhs, Rhs const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
auto operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs const& lhs_;
Rhs const& rhs_;
};
我的目标是实现一个向量 class Vec
,它允许像 auto vecRes = vecA + vecB * vecC
这样的算术表达式的高效计算。这是一个已知问题,可以在维基百科上找到使用 Curiously Recurring Template Pattern (CRTP) 的解决方案。
我首先采用硬编码向量元素类型 double
的实现,包括用于添加的派生 class,
template <typename Exn>
class VecExn
{
public:
double operator[] ( int32_t idx ) const { return static_cast<Exn const&>( *this )[idx]; }
int32_t size() const { return static_cast<Exn const&>( *this ).size(); }
};
class Vec: public VecExn<Vec>
{
public:
Vec() {}
Vec( std::initializer_list<double> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) : eles_( exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
double operator[] ( int32_t idx ) const { return eles_[idx]; }
double& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<double> eles_;
};
template <typename Lhs, typename Rhs>
class VecSum: public VecExn<VecSum<Lhs, Rhs> >
{
public:
VecSum( Lhs const& lhs, Rhs const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
double operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs const& lhs_;
Rhs const& rhs_;
};
正常工作。
但是,在我用模板替换 double
的方法中,基础 class 获得了模板模板参数,而普通 Vec
当然变成了模板化。
template < typename Typ, template<typename> typename Exn >
class VecExn
{
public:
Typ operator[] ( int32_t idx ) const { return static_cast<Exn<Typ> const&>( *this )[idx];}
int32_t size() const { return static_cast<Exn<Typ> const&>( *this ).size(); }
};
template <typename Typ>
class Vec: public VecExn<Typ, Vec >
{
public:
Vec() {}
Vec( std::initializer_list<Typ> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) :eles_(exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
Typ operator[] ( int32_t idx ) const { return eles_[idx]; }
Typ& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<Typ> eles_;
};
问题出现在 VecSum
class 定义中,该定义在硬编码 double
案例中已经是模板 class,现在不被识别为VecExn
.
template <typename Typ, template <typename> typename Lhs, template <typename> typename Rhs>
class VecSum: public VecExn<Typ, VecSum<Typ, Lhs, Rhs> > // ERROR: does not match the template parameter list for template parameter 'Exn'
{
public:
VecSum( Lhs<Typ> const& lhs, Rhs<Typ> const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
Typ operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs<Typ> const& lhs_;
Rhs<Typ> const& rhs_;
};
我该如何解决这个问题?
正如@super 和@Jarod42 指出的那样,解决方案非常简单:
不要在基础 class 或运算符的表达式模板中使用模板模板参数,而是替换 return 类型的 double operator[]
来自 auto
.
template <typename Exn>
class VecExn
{
public:
auto operator[] ( int32_t idx ) const { return static_cast<Exn const&>( *this )[idx]; }
int32_t size() const { return static_cast<Exn const&>( *this ).size(); }
};
template <typename Typ>
class Vec: public VecExn<Vec<Typ>>
{
public:
Vec() {}
Vec( std::initializer_list<Typ> iniLis ) : eles_( iniLis ) { }
template < typename Exn1 >
Vec( VecExn<Exn1> const& exn ) : eles_( exn.size() )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
}
template < typename Exn1 >
Vec& operator=( VecExn<Exn1> const& exn )
{
for( int32_t idx = 0; idx < exn.size(); ++idx )
eles_[idx] = exn[idx];
return *this;
}
Typ operator[] ( int32_t idx ) const { return eles_[idx]; }
Typ& operator[] ( int32_t idx ) { return eles_[idx]; }
int32_t size() const { return eles_.size(); };
private:
std::vector<Typ> eles_;
};
template <typename Lhs, typename Rhs>
class VecSum: public VecExn<VecSum<Lhs, Rhs> >
{
public:
VecSum( Lhs const& lhs, Rhs const& rhs ) : lhs_( lhs ), rhs_( rhs ) {}
auto operator[] ( int32_t idx ) const { return lhs_[idx] + rhs_[idx]; }
int32_t size() const { return lhs_.size(); };
private:
Lhs const& lhs_;
Rhs const& rhs_;
};