重构类:无法将派生类中成员函数的公共代码移回基类



我正在尝试实现一个C++矩阵库(https://github.com/statslabs/matrix)根据《C++程序设计语言》第29章"矩阵设计"。发现我不能将MatrixMatrix_ref中完全相同的成员函数移回Matrix_base

基类是一个纯抽象类(https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix_base.h):

template<typename T, size_t N>
class Matrix_base {
public:
// common stuff
virtual T *data() = 0;
virtual const T *data() const = 0;
private:
Matrix_slice<N> desc;  // the shape of the matrix
};

第一个派生类是(https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix.h):

template<typename T, size_t N>
class Matrix : public Matrix_base<T,N> {
// special to Matrix
public:
T *data() { return elements.data(); }  // "flat" element access
const T *data() const { return elements.data(); }
private:
vector<T> elements;
};

第二个派生类是(https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix_ref.h):

template<typename T, size_t N>
class Matrix_ref : public Matrix_base<T,N> {
// special to Matrix_ref
public:
T *data() { return ptr_; }  // "flat" element access
const T *data() const { return ptr_; }
private:
T* ptr;  // the first element in the Matrix
};

MatrixMatrix_ref中的成员函数完全相同:

// m(i,j,k) subscripting with integers
template<typename T, std::size_t N>
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), T &>
Matrix<T, N>::operator()(Args... args) {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
template<typename T, std::size_t N>
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), const T &>
Matrix<T, N>::operator()(Args... args) const {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}

其中matrix_impl::Requesting_element<Args...>()确保Args...中的整数可以转换为std::size_t:

template<bool B, typename T = void>
using Enable_if = typename std::enable_if<B, T>::type;
template<typename X, typename Y>
constexpr bool Convertible() { return std::is_convertible<X, Y>::value; }
constexpr bool All() { return true; }
template<typename... Args>
constexpr bool All(bool b, Args... args) { return b && All(args...); }
template<typename... Args>
constexpr bool Requesting_element() {
return All(Convertible<Args, size_t>()...);
}

当提到的成员函数在MatrixMatrix_ref中时,一切都是好的。然而,当我将所有代码移动到Matrix_base时,似乎以某种方式通过给出以下消息来检查Arg...不再正常工作:

/Users/pany/statslabs-project/matrix/examples/dgemm_example.cpp:33:7: error: no matching function for call to object of type 'slab::mat' (aka 'Matrix<double, 2>')
A(i, j) = (double) (i * k + j + 1);
^
/Users/pany/statslabs-project/matrix/include/slab/matrix/traits.h:24:43: note: candidate template ignored: disabled by 'enable_if' [with Args = <int, int>]
using Enable_if = typename std::enable_if<B, T>::type;
^
/Users/pany/statslabs-project/matrix/include/slab/matrix/traits.h:24:43: note: candidate template ignored: disabled by 'enable_if' [with Args = <int, int>]

如果你想编译这个项目,你可能需要安装cmake和intel MKL(我想实现一些模板函数作为CBLAS的接口(。我原以为这是简单的类重构,但它只需要几个小时,我不明白为什么会发生这种情况。

我找到了解决方案,这似乎是一个重复的问题(如何在模板基类中调用模板成员函数?)。

现在代码已移动到MatrixBase:

template<typename T, std::size_t N>
template<typename... Args>
T &MatrixBase<T, N>::operator()(Args... args) {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
template<typename T, std::size_t N>
template<typename... Args>
const T &MatrixBase<T, N>::operator()(Args... args) const {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}

在派生类MatrixMatrixRef中,我们有

// m(i,j,k) subscripting with integers
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), T &>
operator()(Args... args) {
return MatrixBase<T,N>::template operator()<Args...>(args...);
}
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), const T &>
operator()(Args... args) const {
return MatrixBase<T,N>::template operator()<Args...>(args...);
}

请注意,脚本检查将只在派生类中进行,因为基类是一个纯抽象类,我们永远不会创建MatrixBase的对象。

最新更新