问题
I want to use partial specialization for a template class so that all children of that template class will use that specialization. Let me explain it with an example :)
template < typename T, unsigned int rows, unsigned int cols>
class BaseMatrix {...};
This class will have children that specify the structure of the matrix, like sparse,dense,diagonal,..
template < typename T, unsigned int rows, unsigned int cols>
class DiagonalMatrix : public BaseMatrix<T,rows,cols>{..}
Then those classes will have children again that specify the storage : stack arrays, vectors, list, queues,..
template < typename T, unsigned int rows, unsigned int cols>
class StackDiagonalMatrix : public DiagonalMatrix<T, rows, cols> {..}
Then there is a class Matrix, which provides all the mathematical functionality. This template class implements operator+, operator-, etc...
template <typename T,
template<typename, unsigned, unsigned> class MatrixContainer,
unsigned Rows,
unsigned Cols>
class matrix;
For this last class I want to write specializations like this:
template <typename T,unsigned Rows, unsigned Cols>
class matrix<T, BaseMatrix, Rows, Cols> {};
template <typename T,unsigned Rows, unsigned Cols>
class matrix<T, DiagonalMatrix, Rows, Cols> {};
But when I write a StackDiagonalMatrix which inherits from DiagonalMatrix, it does not find the specialization for DiagonalMatrix -- it does not find a specialization at all actually.
error: aggregate ‘matrix<int, StackDenseMatrix, 3u, 2u> matrix’ has incomplete type and cannot be defined
Now is there a solution for this problem? Can you write a specialization for a parent of several template classes?
Many thanks!
Full Source that is involved :
template <typename T, unsigned int rows, unsigned int cols>
class BaseMatrix {
protected:
BaseMatrix(){};
static const unsigned rowSize = rows;
static const unsigned colSize = cols;
};
template <typename T, unsigned int rows, unsigned int cols>
class DenseMatrix : public BaseMatrix<T, rows, cols> {
protected:
DenseMatrix(){};
};
template <typename T, unsigned int rows, unsigned int cols>
class StackDenseMatrix : public DenseMatrix<T, rows, cols> {
public:
typedef T value_type;
private:
value_type grid[rows][cols];
StackDenseMatrix();
};
template<typename value_type, unsigned int rows, unsigned int cols>
StackDenseMatrix<value_type, rows,cols>::StackDenseMatrix () {
for (unsigned int i = 0; i < this->rowSize; i++) {
for (unsigned int j = 0; j < this->colSize; j++) {
grid[i][j] = 0;
}
}
}
template <typename T, template<typename, unsigned, unsigned> class MatrixContainer ,unsigned Rows, unsigned Cols>
class matrix;
template <typename T,unsigned Rows, unsigned Cols>
class matrix<T,BaseMatrix, Rows, Cols> {
matrix(){};
};
int main () {
matrix<int, StackDenseMatrix, 3, 2> matrix;
return 0;
}
回答1:
Inheritance does not apply to template specializations. When you invoke matrix<int,StackDenseMatrix,3,2> matrix;
, it will select the general template for matrix
, because the second argument is StackDenseMatrix
, not BaseMatrix
. Even though those classes are related through inheritance, it doesn't make any difference, they are not of the exact same type, so the compiler won't select the specialization of matrix
.
To solve your problem, I don't think inheritance will do you any good in this case. In generic programming, the more appropriate tools are type-traits, policies and concepts. In this case, you should be able to apply some type-traits to achieve similar goals. One trick I like to use is the default template argument that depends on a previous template argument, and then do a partial specialization. As follows for example:
enum MatrixStorage {
DenseMatrix,
SparseMatrix
};
enum MatrixStructure {
GeneralMatrix,
SquareMatrix,
DiagonalMatrix //, ...
};
template <typename T, unsigned Rows, unsigned Cols>
class StackDenseMatrix {
public:
typedef T value_type;
static const MatrixStorage Storage = DenseMatrix;
static const MatrixStructure Structure = GeneralMatrix;
//..
};
//General template with default arguments:
template <typename T,
template <typename, unsigned, unsigned> class MatrixContainer,
unsigned Rows, unsigned Cols,
MatrixStorage Storage = MatrixContainer<T,Rows,Cols>::Storage,
MatrixStructure Structure = MatrixContainer<T,Rows,Cols>::Structure>
class matrix;
//Specialization with given arguments:
template <typename T,
template <typename, unsigned, unsigned> class MatrixContainer,
unsigned Rows, unsigned Cols>
class matrix<T,MatrixContainer,Rows,Cols,DenseMatrix,GeneralMatrix> {
//implementation of matrix for when the container is dense and has general structure...
};
int main() {
matrix<int,StackDenseMatrix,3,2> M; //no extra arguments, and the right specialization will be selected based on the traits of StackDenseMatrix.
return 0;
};
I have my own matrix library that heavily relies on template meta-programming and generic programming techniques along the lines of the above example to provide special implementation of matrix operations for different type of matrix structures and storage, and it works very well this way. I used to use inheritance for the different matrix types, but I have now switched to relying only on type-traits, concepts, policies and Sfinae switches, that is a much more practical solution.
回答2:
To solve your problem, you can use policy-based design. You can make for instance policy classes of Storage and Shape.
class Diagonal {
public:
// the default storage facility of a Diagonal matrix
typedef Stack default_storage;
};
template <typename T, typename Shape = DefaultShape, typename Storage = DefaultStorage, unsigned Row, unsigned Col>
class Matrix : public Storage, Shape { // policy classes Storage and Shape
public:
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T1,Shape1,Storage1,Row1,Col1>& operator += (Matrix<T1,Shape1,Storage1,Row1,Col1> matrix1,Matrix<T,Shape,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T1,Diagonal,Storage1,Row1,Col1>& operator += (Matrix<T1,Diagonal,Storage1,Row1,Col1> matrix1,Matrix<T,Diagonal,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T,Shape,Storage,Row,Col>& operator + (Matrix<T,Shape,Storage,Row,Col> matrix1, Matrix<T,Shape,Storage,Row,Col> matrix2);
template <typename T1, typename Shape1, typename Storage1, unsigned Row1, unsigned Col1>
friend Matrix<T,Diagonal,Storage,Row,Col>& operator + (Matrix<T,Diagonal,Storage,Row,Col> matrix1, Matrix<T,Diagonal,Storage,Row,Col> matrix2);
// general template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Shape,Storage,Row,Col>& operator + (Matrix<T,Shape,Storage,Row,Col> matrix1, Matrix<T,Shape,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>();
for (unsigned i = 0; i < matrix1.getRowSize(); i++) { // getRowSize is a member function of policy class Storage
for (unsigned j = 0; j < matrix1.getRowSize(); j++) {
(*result)(i,j) = matrix1.getValue(i,j) + matrix2.getValue(i,j);
}
}
return *result;
}
// overloaded template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Diagonal,Storage,Row,Col>& operator + (Matrix<T,Diagonal,Storage,Row,Col> matrix1, Matrix<T,Diagonal,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>();
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
(*result)(i,i) = matrix1.getValue(i,i) + matrix2.getValue(i,i);
}
return *result;
}
// general template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Shape,Storage,Row,Col>& operator += (Matrix<T,Shape,Storage,Row,Col> matrix1,Matrix<T,Shape,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>(matrix1); // copy constructor
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
for (unsigned j = 0; j < matrix1.getRowSize(); j++) {
(*result)(i,j) = matrix1.getValue(i,j) + matrix2.getValue(i,j);
}
}
return *result;
}
// overloaded template function
template <typename T, typename Shape, typename Storage, unsigned Row, unsigned Col>
Matrix<T,Diagonal,Storage,Row,Col>& operator += (Matrix<T,Diagonal,Storage,Row,Col> matrix1,Matrix<T,Diagonal,Storage,Row,Col> matrix2) {
Matrix<T,Shape,Storage,Row,Col>* result = new Matrix<T,Shape,Storage,Row,Col>(matrix1); // copy constructor
for (unsigned i = 0; i < matrix1.getRowSize(); i++) {
(*result)(i,i) = matrix1.getValue(i,i) + matrix2.getValue(i,i);
}
return *result;
}
The major advantage of using policy-based design here is that your users can easily provide their own storage facilities and shape operations. All you need to do is give them a clear interface so that they know how they can make their own storage facilities and shape operations.
来源:https://stackoverflow.com/questions/6154844/partial-specialization-for-a-parent-of-multiple-classes