g++ has some troubles with nested templates

Brian White bcwhite@verisim.com
Tue Jun 30 10:03:00 GMT 1998


[ This was originally submitted as a report in the Debian bug tracking
  system.  Please cc: 21255-forwarded@bugs.debian.org when replying to
  this message so all relevant information gets logged correctly.  Thanks! ]


Package: g++
Version: 2.90.27-0.3

I've been experimenting with writing my own "Matrix" class.  I'm having
some problems, though, with the "operator*" because defining a template
method within the template class doesn't seem to work properly.

Here was my first attempt:

===============================================================================
template <class Type, unsigned Width, unsigned Height>
class Matrix {
    Type    Values[Height][Width];

public:
    Type const& operator()(unsigned row, unsigned col) const { return Values[row][col]; }
    Type&       operator()(unsigned row, unsigned col)       { return Values[row][col]; }

    template <unsigned Size>
    Matrix<Type,Size,Width> operator* (Matrix<Type,Height,Size> const& rhs) const;
};



template <class Type, unsigned Width, unsigned Height, unsigned Size>
inline
Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator*(Matrix<Type,Height,Size> const& rhs) const
{
    Matrix<Type,Size,Width> result;

    for (unsigned w=0; w < Width; w++) {
        for (unsigned s=0; s < Size; s++) {
            for (unsigned h=0; h < Height; h++) {
                result(s,w) += Values[h][w] * rhs(h,s);
            }
        }
    }

    return result;
}



int main(int, char**)
{
    Matrix<double,4,4>  transform;
    Matrix<double,1,4>  input;
    Matrix<double,4,1>  output;

    /* ... initialize matricies ... */

    output = input * transform;

    return 1;
}
-------------------------------------------------------------------------------
dragon:~/tmp> c++ -Wall template-test.cc -o /dev/null
template-test.cc: In method `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const':
template-test.cc:18: template definition of non-template `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const'
dragon:~/tmp> 
===============================================================================

As you can see, the compiler doesn't even recognize that the Matrix class
(as defined by only three parameters) exists.


So, I changed the 4-parameter template into a 1-parm template of a 3-parm
template and got the following:

===============================================================================
template <class Type, unsigned Width, unsigned Height>
class Matrix {
    Type    Values[Height][Width];

public:
    Type const& operator()(unsigned row, unsigned col) const { return Values[row][col]; }
    Type&       operator()(unsigned row, unsigned col)       { return Values[row][col]; }

    template <unsigned Size>
    Matrix<Type,Size,Width> operator* (Matrix<Type,Height,Size> const& rhs) const;
};



template <unsigned Size>
template <class Type, unsigned Width, unsigned Height>
inline
Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator*(Matrix<Type,Height,Size> const& rhs) const
{
    Matrix<Type,Size,Width> result;

    for (unsigned w=0; w < Width; w++) {
        for (unsigned s=0; s < Size; s++) {
            for (unsigned h=0; h < Height; h++) {
                result(s,w) += Values[h][w] * rhs(h,s);
            }
        }
    }

    return result;
}



int main(int, char**)
{
    Matrix<double,4,4>  transform;
    Matrix<double,1,4>  input;
    Matrix<double,4,1>  output;

    /* ... initialize matricies ... */

    output = input * transform;

    return 1;
}
-------------------------------------------------------------------------------
dragon:~/tmp> c++ -Wall template-test.cc -o /dev/null
template-test.cc:19: prototype for `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const' does not match any in class `Matrix<Type,Width,Height>'
template-test.cc:10: candidate is: template <unsigned int const Size> class Matrix<Type,Type,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Type> &) const
template-test.cc: In method `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const':
template-test.cc:19: must specialize `class Matrix<Type,Width,Height>' before defining member `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const'
template-test.cc:19: template definition of non-template `class Matrix<Type,Size,Width> Matrix<Type,Width,Height>::operator *(const class Matrix<Type,Height,Size> &) const'
dragon:~/tmp> 
===============================================================================

It still didn't work, but the error is different.  Now, it shows that the
original definition was not parsed correctly.  The "Size" parameter of the
inner template gets confused with the "Type" parameter of the outer
template, probably because both are the first parameter of their respective
templates.  You'll notice that

    Matrix<Type,Size,Width> operator* (Matrix<Type,Height,Size> const&)

in the original source got translated to canditate

    Matrix<Type,Type,Width> operator* (Matrix<Type,Height,Type> const&)

when the compiler printed its diagnostic.  (Note that this line doesn't
appear literally in the compiler output -- I copied and edited the raw
output above to make the mistake more obvious.)


Moving right along...  I figured perhaps I would have better luck if I
defined the procedure right in the class instead of as a template inline
later on.

===============================================================================
template <class Type, unsigned Width, unsigned Height>
class Matrix {
    Type    Values[Height][Width];

public:
    Type const& operator()(unsigned row, unsigned col) const { return Values[row][col]; }
    Type&       operator()(unsigned row, unsigned col)       { return Values[row][col]; }

    template <unsigned Size>
    Matrix<Type,Size,Width> operator* (Matrix<Type,Height,Size> const& rhs) const
    {
        Matrix<Type,Size,Width> result;

        for (unsigned w=0; w < Width; w++) {
            for (unsigned s=0; s < Size; s++) {
                for (unsigned h=0; h < Height; h++) {
                    result(s,w) += Values[h][w] * rhs(h,s);
                }
            }
        }

        return result;
    }
};



int main(int, char**)
{
    Matrix<double,4,4>  transform;
    Matrix<double,1,4>  input;
    Matrix<double,4,1>  output;

    /* ... initialize matricies ... */

    output = input * transform;

    return 1;
}
-------------------------------------------------------------------------------
dragon:~/tmp> c++ -Wall template-test.cc -o /dev/null
dragon:~/tmp> 
===============================================================================

This compiles fine!


The next step was to test it.  It doesn't run, but I eventually tracked that
back to a dumb mistake I made in the actual multiply algorithm.  Oops!

Please let me know if there is any further information I can supply.

                                          Brian
                                 ( bcwhite@verisim.com )

-------------------------------------------------------------------------------
                  Don't drink and park.  Accidents cause kids.





More information about the Gcc-bugs mailing list