Good day!

I faced the following problem: I am writing a class of matrices, and I would like it to be possible to set both matrix dimensions fixed in compile-time and dynamically changeable. The whole project should be in the spirit of boost or stl, so I wrote two classes of arrays that are used in the matrix class to store values. Here is their shortened announcement:

template< typename T, typename _Derived > struct _Vector_base // Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΎΠ²: ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ для typedef { public: typedef _Vector_base<T, _Derived> base_type; typedef T value_type; typedef size_t size_type; typedef T* pointer; typedef const T* const_pointer; typedef pointer iterator; typedef const_pointer const_iterator; }; template< typename _Scalar > class vector : // класс динамичСского Π²Π΅ΠΊΡ‚ΠΎΡ€Π° _Vector_base<_Scalar, vector<_Scalar> > { // ... }; template< typename _Scalar, size_t _Size > class fixed_vector : // класс фиксированного Π²Π΅ΠΊΡ‚ΠΎΡ€Π° _Vector_base<_Scalar, fixed_vector<_Scalar, _Size> > { // ... }; template< typename _Scalar, typename _Container > class _Matrix // Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ класс всСх ΠΌΠ°Ρ‚Ρ€ΠΈΡ† - typedef'Ρ‹ ΠΈ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ { public: typedef _Matrix<_Scalar, _Container> base_type; typedef size_t size_type; typedef _Scalar value_type; typedef _Scalar* pointer; typedef _Scalar& reference; typedef const _Scalar* const_pointer; typedef const _Scalar& const_reference; typedef _Container container_type; _Matrix() : nrows(0), ncols(0) { } _Matrix(size_t n, size_t m) : nrows(n), ncols(m){ } inline size_type size() const { return (nrows * ncols); } inline size_type rows() const { return nrows; } inline size_type columns() const { return ncols; } inline bool empty() const { return ((nrows == 0) && (ncols == 0)); } protected: size_t nrows, ncols; _Container m; }; // end class _Matrix // класс динамичСских ΠΌΠ°Ρ‚Ρ€ΠΈΡ† (ΠΎΠ±Ρ‰ΠΈΠΉ случай) template< typename _Scalar, typename _Container = vector<_Scalar> > class matrix : public _Matrix<_Scalar, _Container> { public: matrix(size_t _Rows, size_t _Cols) : _Matrix(_Rows, _Cols) { } }; // частичная спСциализация класса ΠΌΠ°Ρ‚Ρ€ΠΈΡ† для ΠΌΠ°Ρ‚Ρ€ΠΈΡ† фиксированного Ρ€Π°Π·ΠΌΠ΅Ρ€Π° template< typename _Scalar, size_t _Rows, size_t _Cols > class matrix<_Scalar, fixed_vector<_Scalar, (_Rows * _Cols)> > : public _Matrix<_Scalar, fixed_vector<_Scalar, _Rows*_Cols> > { public: matrix() : _Matrix(_Rows, _Cols) { } }; 

I assumed that the use would be something like this:

 int main(int argc, char * argv[]) { matrix<float> m(2, 2); // динамичСская ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π° matrix<float, 2, 2> x; // фиксированная ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π° return 0; } 

However, the compiler (MS Visual Studio 2012) does not skip this:

 ClCompile: main.cpp ..\main.cpp(62): error C2977: 'matrix' : too many template arguments ..\main.cpp(12): see declaration of 'matrix' ..\main.cpp(62): error C2133: 'x' : unknown size ..\main.cpp(62): error C2512: 'matrix' : no appropriate default constructor available Build FAILED. 

Actually the question is: is it possible to compile this or something contrary to the rules of C ++? And if so how to do it?

In principle, I could make a separate class for fixed matrices (for example, fixed_matrix<T, N> ), but it is still interesting to find out why this particular option does not work.

    1 answer 1

    No, no, partial specialization doesn't work that way.

    The idea of ​​partial specialization is that you have a fixed list of template arguments, but for some cases it is revealed differently. For example:

     template<typename T> class Container { // имплСмСнтация ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ }; template<typename T> class Container<T*> { // ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ имплСмСнтация для указатСля }; template<typename ARG, typename RET> class Container<RET(ARG)> { // ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ имплСмСнтация для Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ARG -> RET }; 

    When you write, for example, Container<int*> , the compiler checks: does any special case work? Could the type int* represented as T* for some T ? If it turns out that it is possible (for T = int ), the compiler uses the necessary implementation.

    Now for your case. You can try this trick:

     template< typename _Scalar, typename _Container, size_t _Rows = 0, size_t _Cols = 0 > // ΠΎΠ±Ρ‰ΠΈΠΉ случай, Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π΅ Π½ΡƒΠ»Π΅Π²ΠΎΠΉ, фиксированный class matrix : public _Matrix<_Scalar, _Container> { public: matrix() : _Matrix(_Rows, _Cols) { static_assert(_Rows > 0 && _Cols > 0, "both _Rows and _Cols must be either 0 or > 0"); } }; template< typename _Scalar, > class matrix<_Scalar, _Container, 0, 0> : public _Matrix<_Scalar, _Container> { public: matrix(size_t _Rows, size_t _Cols) : _Matrix(_Rows, _Cols) { } }; 

    (Here is the working implementation: http://ideone.com/kK66HH .)

    If this does not work, you can always simply declare two different templates: matrix and fixed_matrix .