Is this a GCC bug or I am doing something wrong

Started by
7 comments, last by Misery 11 years, 2 months ago

Hello,

I have a peculiar problem. I have written a simple matrix class that uses expression templates. On Microsoft C++ and Intel C++ compilers it works just fine. However on Linux GCC it works quite unexpectedly:

When trying to use a line of code using expression templates I get an error:


error: no matching function for call to ‘mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >::MatrixMatrixElementWiseOperation()’
 

and debugger points do Matrix class move constructor (which is absolutely different class). However if I remove the definition of the move constructor and leave only its declaration everything works fine. But i have no idea what is really happenning there, as there should undefined reference error occur. Nothing like that happens. To make the following code work, just comment out Matrix move constructor definition, and leave its declaration only.

Below, used code samples:

main.cpp:


#include <iostream>
#include "../mfmlMatrixTemplate.h"
#include "../mfmlMatrixOperatorsET.h"
using namespace std;

int main()
{
    mfml::Matrix<double, unsigned long> x,y,z;
    x.Resize(10,1);
    y.Resize(10,1);

    z=x+y;
    cout << "done" << endl;
    return 0;
}
 

DataContainer class:


#ifndef MFML_DATA_CONTAINER_H_INCLUDED
#define MFML_DATA_CONTAINER_H_INCLUDED
namespace mfml{
template <class DATA_TYPE,class INT_TYPE>
class DataContainer
{
  public:
      DATA_TYPE *Data;
      INT_TYPE Rows,Cols;

      DataContainer() : Data(NULL), Rows(INT_TYPE(0)), Cols(INT_TYPE(0))
      {

      }

      DataContainer(const DataContainer &&dc)
      {
        Data=NULL;
        Rows=0;
        Cols=0;

        std::swap(Data,dc.Data);
        std::swap(Rows,dc.Rows);
        std::swap(Cols,dc.Cols);
      }


     ~DataContainer()
     {
        Free();
     }

    inline DATA_TYPE operator[] (INT_TYPE i) const  { return Data[i];  }
    inline DATA_TYPE& operator[] (INT_TYPE i)  { return Data[i]; }

    inline void Alloc(INT_TYPE rows,INT_TYPE cols)
    {
      if (rows>0 && cols>0)
      {
        Data=new DATA_TYPE[rows*cols];
        Rows=rows;
        Cols=cols;
      }
    }

    inline void Free()
    {
       delete[] Data;
       Data=NULL;
       Rows=0;
       Cols=0;
    }

    inline INT_TYPE NumOfElements() const  { return Rows*Cols;  }
    inline INT_TYPE NumOfRows() const { return Rows; }
    inline INT_TYPE NumOfCols() const { return Cols; }

  private:
    DataContainer(const DataContainer &dc) {}
    DataContainer& operator=(const DataContainer &dc)
    {
      return *this;
    }
};
} //namespace mfml

#endif // MFML_DATA_CONTAINER_H_INCLUDED
 



Matrix class:


#ifndef MFML_MATRIX_TEMPLATE_H_INCLUDED
#define MFML_MATRIX_TEMPLATE_H_INCLUDED
//-------------------C++ INCLUDES--------------------------BEGIN
#include <algorithm>
#include <vector>
#include <iostream>
#include <cmath>
#include <functional>


#ifdef _OPENMP
   #include <omp.h>
#endif
//-------------------C++ INCLUDES--------------------------END

//-------------------MFML INCLUDES-------------------------BEGIN
#include "mfmlDataContainer.h"


//-------------------MFML INCLUDES-------------------------END
namespace mfml
{
    template <class DATA_TYPE,class INT_TYPE=std::size_t,class DATA_CONTAINER=DataContainer<DATA_TYPE,INT_TYPE> >
    class Matrix
    {
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       private:
         DATA_CONTAINER dc;
         inline void Alloc(INT_TYPE _Rows,INT_TYPE _Cols);  //Allocates the memory for the Matrix.Data - to be used only when matrix is created, otherwise use Resize(RequiredRowsNumber,RequiredColumnsNumber)
         inline void Free(); //Frees memory
         Matrix(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &M); //Copy cnstructor not to be used, therefore it is private. operator A=B should be used to copy
      //   Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &&M); //Move constructor
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       public:
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         Matrix();  //Default constructor, creates an empty Matrix instance
         Matrix(DATA_TYPE s); //Meritoric constructor with scalar value - creates matrix of size 1x1
         Matrix(const DATA_CONTAINER &_dc);
         Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &&M);
         ~Matrix(); //Destructor
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         inline void Resize(INT_TYPE _Rows,INT_TYPE _Cols); 
         inline void ResizeNoCopyNoFill(INT_TYPE _Rows,INT_TYPE _Cols); 
         inline void Copy(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &M); 
         inline operator DATA_TYPE() const;  //Easy conversion from Matrix of size 1x1 to scalar s=M

         DATA_TYPE operator[](INT_TYPE i) const;
         inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& operator =(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& M);
         inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& operator =(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>&& M);
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         inline DATA_TYPE* GetDataPtr() const {return dc.Data;} //Method returns pointer to Data, method required for compatibility with Basic Operations (bo:: namespace) functions
         inline const DATA_CONTAINER& dcRef() const {return dc;}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         inline INT_TYPE NumOfElements()const {return dc.NumOfElements();} //Method returns the number of elements stored in the matrix
         inline INT_TYPE NumOfRows() const {return dc.NumOfRows();} //Method returns the number of rows in the matrix
         inline INT_TYPE NumOfCols() const {return dc.NumOfCols();} //Method returns the number of columns in the matrix
         inline INT_TYPE& RowsRef() {return dc.Rows;};
         inline INT_TYPE& ColsRef() {return dc.Cols;};
         inline bool IsEmpty() {return (dc.Rows==0 && dc.Cols==0);}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

         template <typename Expr>
         Matrix& operator = (const Matrix<DATA_TYPE,INT_TYPE,Expr>& Rhs)
         {
            Resize(Rhs.NumOfRows(),Rhs.NumOfCols());
            #ifdef mfmlSerialOnly
               for (INT_TYPE i = 0; i < Rhs.NumOfElements(); i++) dc[i] = Rhs[i];
            #else
               INT_TYPE NE=Rhs.NumOfElements();
               if (NE<100)
               {
                    for (INT_TYPE i = 0; i < NE; i++) dc[i] = Rhs[i];
               }
               else
               {
                   DATA_CONTAINER &dcp=dc;
                   #pragma omp parallel for default(none) shared(dcp,Rhs,NE)
                   for (INT_TYPE i = 0; i < NE; i++) dcp[i] = Rhs[i];
               }
            #endif
            return *this;
         }

    };  //CLASS DEFINITION END

    //DEFAULT CONSTRUCTOR
    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix() : dc()
    {   }

    //COPY CTR - PRIVATE TO AVOID USING IT
    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &M) //: dc(M.dc)
    {
        std::cout<<"cpy ctr"<<std::endl;
    }
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(const DATA_CONTAINER &_dc) : dc(_dc)
    { std::cout<<"Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(const DATA_CONTAINER &_dc)"<<std::endl;  }
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

/*=============================================================================================================
COMMENT THE FOLLOWING DEFINITION TO MAKE EVERYTHING WORK
=============================================================================================================*/

    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &&M)
    {
       std::cout<<"move ctr"<<std::endl;
       dc.Data=NULL;
       dc.Rows=0;
       dc.Cols=0;
       std::swap(dc.Data,M.dc.Data);
       std::swap(dc.Rows,M.dc.Rows);
       std::swap(dc.Cols,M.dc.Cols);
    }

//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/*
    Destructor. Removes the memory allocated previously.
*/
    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::~Matrix()
    {
        Free();
    }

    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline void Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Alloc(INT_TYPE _Rows,INT_TYPE _Cols)
    {
       dc.Alloc(_Rows,_Cols);
    }

    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline void Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Free()  //deallocates memory
    {
        dc.Free();
    }

    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline void Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Resize(INT_TYPE _Rows,INT_TYPE _Cols)  //resizes the matrix but doesnt copy the values nor fills the empty places
    {
        if (dc.Rows!=_Rows || dc.Cols!=_Cols)
        {
            INT_TYPE NewNumberOfElements=_Rows*_Cols;
            if (dc.Rows==0 && dc.Cols==0) Alloc(_Rows,_Cols); //data hasn't been initialized, or was deleted
            else if (_Rows==0 || _Cols==0) Free(); //if one of dimensions is to be zero - delete matrix
            else if (NewNumberOfElements!=NumOfElements()) //else - resize and copy values
            {
                DATA_TYPE *tmp=new DATA_TYPE[NewNumberOfElements];
                delete[] dc.Data;
                dc.Data=tmp;
                dc.Rows=_Rows;
                dc.Cols=_Cols;
           }
           else //size hasn't changed, only change od Rows/Columns is required
           {
               dc.Rows=_Rows;
               dc.Cols=_Cols;
           }
        }
    }


       template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
       inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::operator =(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& M)
       {
         std::cout<<"copy operator"<<std::endl;
         Copy(M);
         return *this;
       }
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/*
       Move operator.
*/
       template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
       inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>& Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::operator =(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>&& M)
       {
         std::cout<<"move operator"<<std::endl;
         if (this!=&M)
         {
             std::swap(dc.Rows,M.dc.Rows);
             std::swap(dc.Cols,M.dc.Cols);
             std::swap(dc.Data,M.dc.Data);
         }
         return *this;
       }



    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline void Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Copy(const Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &M)  //copies matrix M to the current matrix
    {
        if (&M!=this)
        {
          if (!M.IsEmpty())
          {
            INT_TYPE MNumOfElements=M.NumOfElements();
            if (MNumOfElements!=NumOfElements())
            {
              Free();
              Alloc(M.dc.Rows,M.dc.Cols);
            }
            else
            {
              dc.Rows=M.dc.Rows;
              dc.Cols=M.dc.Cols;
            }
            #if ((defined WINDOWS_MSVCC_32) || (defined WINDOWS_MSVCC_64))
               std::copy(M.dc.Data,M.dc.Data+MNumOfElements,stdext::checked_array_iterator<DATA_TYPE*>( dc.Data, MNumOfElements ));
            #else
               std::copy(M.dc.Data,M.dc.Data+MNumOfElements,dc.Data);
            #endif
          } else Free();
        }
    }


    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    DATA_TYPE Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::operator[](INT_TYPE i) const
    {
            return dc[i];
    }


} //namespace mmml
#endif // MATRIX_H_INCLUDED
 

Expression template:


#ifndef MFML_EXPRESSION_TEMPLATES_H_INCLUDED
#define MFML_EXPRESSION_TEMPLATES_H_INCLUDED

#include <cmath>

namespace mfml{
namespace meta{
     template<class DATA_TYPE>
     struct plus
     {
        inline static DATA_TYPE apply(DATA_TYPE a,DATA_TYPE b) { return a+b; }
     };
} //namespace meta

     template <class DATA_TYPE,class INT_TYPE, class Lhs, class Op, class Rhs>
     class MatrixMatrixElementWiseOperation
     {
        public:
           inline MatrixMatrixElementWiseOperation(const Lhs& a, const Rhs& b)  : Op1(a)  , Op2(b)
            {
            }
            inline DATA_TYPE operator[] (INT_TYPE i) const {return Op::apply(Op1[i],Op2[i]); }
            inline INT_TYPE NumOfElements() const {  return Op1.NumOfElements(); }
            inline INT_TYPE NumOfRows() const     {  return Op1.NumOfRows();    }
            inline INT_TYPE NumOfCols() const     {  return Op1.NumOfCols();    }
            void Free () {}
            ~MatrixMatrixElementWiseOperation() {}
        private:
            const Lhs& Op1;
            const Rhs& Op2;
};

} //namespace mfml
#endif // MFMLEXPRESSIONTEMPLATES_H_INCLUDED
 


Expression template operator:


#ifndef MFML_MATRIX_OPERATORS_ET_H
#define MFML_MATRIX_OPERATORS_ET_H
/*
  This file contains definitions and sources of EXPRESSION TEMPLATE versions of operators and functions
*/

#include "mfmlMatrixExpressionTemplates.h"
namespace mfml{

        template <class DATA_TYPE,class INT_TYPE, class  Lhs, class Rhs>
        inline Matrix<DATA_TYPE,INT_TYPE,MatrixMatrixElementWiseOperation<DATA_TYPE,INT_TYPE,Lhs,meta::plus<DATA_TYPE>,Rhs> >
                                                                     operator + (const Matrix<DATA_TYPE,INT_TYPE, Lhs>& lhs, const Matrix<DATA_TYPE,INT_TYPE, Rhs>& rhs)
        {
            std::cout<<"dupa.8"<<std::endl;
             if (lhs.NumOfRows()!=rhs.NumOfRows() || lhs.NumOfCols()!=rhs.NumOfCols())
             {
                std::cout<<"throwing InconsistientRowColumnDimensionsException"<<std::endl;
             }
              return Matrix<DATA_TYPE,INT_TYPE,MatrixMatrixElementWiseOperation<DATA_TYPE,INT_TYPE,Lhs,meta::plus<DATA_TYPE>,Rhs> >
                                                    (MatrixMatrixElementWiseOperation<DATA_TYPE,INT_TYPE,Lhs,meta::plus<DATA_TYPE>,Rhs>(lhs.dcRef(), rhs.dcRef()));
        }
} //namespace mfml
#endif
 

Regards,

Misery

Advertisement

with what version ? maybe trying to update it...

how about clang ? (not sure about c++11 support of clang)

did you ensure using std=c++11 on the command line ? (just throwing random stuffs...)

You should always post full error message instead of one line. For example "g++ -std=c++11 shows me following":


a.cpp: In instantiation of 'mfml::Matrix<DATA_TYPE, INT_TYPE, DATA_CONTAINER>::Matrix(mfml::Matrix<DATA_TYPE, INT_TYPE, DATA_CONTAINER>&&) [with DATA_TYPE = double; INT_TYPE = long unsigned int; DATA_CONTAINER = mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >]':
a.cpp:326:241:   required from 'mfml::Matrix<DATA_TYPE, INT_TYPE, mfml::MatrixMatrixElementWiseOperation<DATA_TYPE, INT_TYPE, Lhs, mfml::meta::plus<DATA_TYPE>, Rhs> > mfml::operator+(const mfml::Matrix<DATA_TYPE, INT_TYPE, Lhs>&, const mfml::Matrix<DATA_TYPE, INT_TYPE, Rhs>&) [with DATA_TYPE = double; INT_TYPE = long unsigned int; Lhs = mfml::DataContainer<double, long unsigned int>; Rhs = mfml::DataContainer<double, long unsigned int>]'
a.cpp:338:9:   required from here
a.cpp:165:107: error: no matching function for call to 'mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >::MatrixMatrixElementWiseOperation()'
a.cpp:165:107: note: candidates are:
a.cpp:299:19: note: mfml::MatrixMatrixElementWiseOperation<DATA_TYPE, INT_TYPE, Lhs, Op, Rhs>::MatrixMatrixElementWiseOperation(const Lhs&, const Rhs&) [with DATA_TYPE = double; INT_TYPE = long unsigned int; Lhs = mfml::DataContainer<double, long unsigned int>; Op = mfml::meta::plus<double>; Rhs = mfml::DataContainer<double, long unsigned int>]
a.cpp:299:19: note:   candidate expects 2 arguments, 0 provided
a.cpp:296:12: note: constexpr mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >::MatrixMatrixElementWiseOperation(const mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >&)
a.cpp:296:12: note:   candidate expects 1 argument, 0 provided 

As you can see it doesn't find following member:


error: no matching function for call to 'mfml::MatrixMatrixElementWiseOperation<double, long unsigned int, mfml::DataContainer<double, long unsigned int>, mfml::meta::plus<double>, mfml::DataContainer<double, long unsigned int> >::MatrixMatrixElementWiseOperation()' 

Why?

Your Matrix class has following move constructor:


    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &&M)
    {
       std::cout<<"move ctr"<<std::endl;
       dc.Data=NULL;
       dc.Rows=0;
       dc.Cols=0;
       std::swap(dc.Data,M.dc.Data);
       std::swap(dc.Rows,M.dc.Rows);
       std::swap(dc.Cols,M.dc.Cols);
    }
 

Which uses default constructor for dc member.

In operator + you are instantiating following class "Matrix<DATA_TYPE, INT_TYPE, MatrixMatrixElementWiseOperation<...> >".
So DATA_CONTAINER type will be "MatrixMatrixElementWiseOperation<...>", but it doesn't have default constructor! That's why compiler complains about template instantiation in operator +.
You should use std::move in move constructor:

    template <class DATA_TYPE,class INT_TYPE,class DATA_CONTAINER>
    inline Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> &&M)
        : dc(std::move(M.dc))
    {
    }

I know in gcc (mingw anyway) adding the move constructor deletes the copy constructor, so you have to explicitly set the copy constructor to default. I.e., in the class definition in the header, add something like:


Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER>::Matrix(Matrix<DATA_TYPE,INT_TYPE,DATA_CONTAINER> const &) = default;

And that's with the -std=c++11 flag. And, also just throwing this out in case it helps, gcc will tend to bug out when you use classes defined within a function and try to use that with a standard template or a standard algorithm, even though msvc will compile that stuff fine.

Haven't really gone through this code too thoroughly, just throwing out ideas.

@Martins Mozeiko: Thanks for an answer. You must have spent quite a lot of time to help me. Really thanks a lot! :) That is probably the source of the problem. I'll check it out tomorrow as for now I have too much work and it is my off-hours project. However, still the problem of undefined reference error remain. If there is declaration of the move ctr, and no definition such error should be reported. What do you think about that?

Not so much, most time I spent copy&pasting fragments of your code into a.cpp - after that compiler did rest of job (of showing error message :)

You should post minimal and standalone sample that reproduces the problem, it will be easier help that way.

About which declaration and definition of which class constructor are you talking about?

@Martins Mozeiko: Yep! Your tip solved the problem :]

After I did comment out the definition of class Matrix<> move ctr, build was successful and program worked fine. However there was a bare declaration of this move crt left. Just a declaration, and no definition, so I think it should report unresolved reference error or sth similar. Just wondering if that might be a bug worth reporting.

If you are not calling that move constructor, then it's fine. It's perfectly legal only to declare functions or methods and not define them. In pre-C++11 days it was typically used to disable copy constructor and assign operator (together with making methods private:

class NonCopyableClass
{
public:
    ... // public stuff
private:
    NonCopyableClass(const NonCopyableClass&); // no definition needed because this is private
    NonCopyableClass& operator = (const NonCopyableClass&); // no definition also for this
};

In C++11 of course you can use "= delete"

Thanks again :]

This topic is closed to new replies.

Advertisement