2

I have a class Dataset as given below

template <class StorageType> class Dataset  
{
private:
    class Row
    {
        private:
            uInt32 NCol;
            StorageType *Data;
        public:
            Row(){
                Data = NULL;
            }
            void SetCol(uInt32 Col){
                if((Data = new StorageType[Col]) == NULL)
                {
                    cout << "Dataset::memory exhausted ... exiting" << endl;
                    exit(1);
                }
                NCol = Col;
            }
            ~Row(){
                if(Data != NULL)
                {
                    delete []Data;
                }
            }
            StorageType &operator[](uInt32 Col){
                return Data[Col];
            }
    };
    Row *Array;
    uInt32 NRow;
    uInt32 NCol;
public:
    Dataset(uInt32 Row, uInt32 Col)
    {
        if((Array = new Row[Row]) == NULL){
            cerr << "Dataset::memory exhausted ... exiting" << endl;
            exit(1);
        }
        register uInt32 i;
        for(i = 0;i < Row;i++)
        {
            Array[i].SetCol(Col);
        }
        NRow = Row;
        NCol = Col;
    }

    Dataset(Dataset<StorageType> &B)
    {
        NRow = B.GetNoOfRows();
        NCol = B.GetNoOfCols();

        if((Array = new Row[NRow]) == NULL){
            cerr << "Martix::memory exhausted ... exiting" << endl;
            exit(1);
        }

        register uInt32 i,j;

        for(i = 0;i < NRow;i++)
        {
            Array[i].SetCol(NCol);
            for(j = 0;j < NCol;j++)
            {
                Array[i][j] = B[i][j];
            }
        }
    }

    virtual ~Dataset()
    {
        delete[] Array;
    }

    Row &operator[](uInt32 Row){
        return Array[Row]; 
    }

    uInt32 GetNoOfRows() const
    {
        return NRow;
    }

    uInt32 GetNoOfCols() const
    {
        return NCol;
    }



    Dataset<StorageType> operator*(Dataset<StorageType> const &B)
    {
        Dataset<StorageType> Temp(NRow,B.GetNoOfCols());

        if(NCol == B.GetNoOfRows())
        {

            uInt32 Row = B.GetNoOfRows();
            uInt32 Col = B.GetNoOfCols();
            register uInt32 i, j, k;
            register StorageType Product;
        
            for(i = 0;i < NRow;i++)
            {
                for(j = 0;j < Col;j++)
                {
                    Product = 0;
                    for(k = 0;k < Row;k++)
                    {
                        Product += Array[i][k]*B[k][j];  **-->  error here**
                    }
                    
                    Temp[i][j] = Product;
                }
            }
        }
        else
        {
            cerr << "Dataset::matrices aren't compatible for multiplication" << endl;
        }
        return (Temp);
    }

    void operator=(Dataset<StorageType> const &B)
    {
        register uInt32 i, j;

        uInt32 Row = B.GetNoOfRows();
        uInt32 Col = B.GetNoOfCols();

        for(i = 0;i < Row;i++)
        {
            for(j = 0;j < Col;j++)
            {
                Array[i][j] = B[i][j]; **-->  error here**
            }
        }
    }
};

I am getting the following error

passing 'const Dataset' as 'this' argument discards qualifiers[-fpermissive]

at the places marked --> error here. . I need to multiply two Dataset from a caller class, using operator*, and making the argument const is mandatory. How do I resolve it? Some code example will be helpful.

user438383
  • 5,716
  • 8
  • 28
  • 43
Nostalgic
  • 300
  • 1
  • 4
  • 18
  • The B parameter is const, but the operator[] operates on non-const objects. You'll need a second version of operator[] returning a const ref, and declared a const member function. – Peter - Reinstate Monica Aug 22 '21 at 15:58
  • @Peter - Reinstate Monica I need to call the overloaded operator from another place and if I remove const from the B parameter I get another error--> ```cannot bind non-const lvalue reference of type Dataset& to an rvalue of type DataSet``` – Nostalgic Aug 22 '21 at 16:05
  • For a start, it would help if you extracted a [mcve], which includes stripping all the template stuff and also using C++ types, not `uInt32` typedefs. In any case, search for "essential C++ resources" here. The reason is, that there are lots of bad habits in your code which probably come from bad learning resources. For example, if any tutorial teaches you to check the returnvalue from `new` against `NULL`, the tutorial is totally outdated or otherwise bad. – Ulrich Eckhardt Aug 22 '21 at 16:13
  • @UlrichEckhardt After making the code compile with a few trivial edits (change the name of type Row to avoid name collisions, include headers) it compiles just fine. Why is that? Why can I call the non-const operator[] on a const B? (gcc 10.2, VC.) ... https://godbolt.org/z/56necn4rd – Peter - Reinstate Monica Aug 22 '21 at 16:47
  • Does this answer your question? [error: passing xxx as 'this' argument of xxx discards qualifiers](https://stackoverflow.com/questions/5973427/error-passing-xxx-as-this-argument-of-xxx-discards-qualifiers) – Karl Knechtel Aug 22 '21 at 17:46

1 Answers1

0

The code you presented produces a lot more errors, so it cannot have produced only the errors you show.

After fixing the fairly trivial problems the code compiles just fine (until recent compilers start to complain about the forbidden register storage class which I have last used about 1988), see https://godbolt.org/z/56necn4rd .

Clean up your code and present the errors that remain, if any.

I noticed that the error appears to be related to the fact that your class Datasetis a template. A non-templated version of the same class (where I simply replaces StorageType with int) leads to the expected error (calling a non-const member function from a const object).

The reason appears to be that the error only occurs when the template code is actually instantiated, that is, if some code actually calls the const-violating function, directly or indirectly. Here is an example (live at godbolt)).

Technically, the definition of f() is a const violation because the body calls g() from an object that is not const. (The reason this is forbidden is because g() might change the object which the const f() promises not to do.) But templates are special — for example, the SFINAE pattern makes it possible to have code that normally would create a compiler error when creating the list of potential overloads in templates for a given template parameter; those overloads are simply discarded at instantiation time. Something similar may be in play here; it's just that none of the overloads is viable, but that is only detected (and relevant) at instantiation time.

#include <iostream>
using namespace std;

template<class T> struct TC
{
    void g() {}
    // We can violate constness in member functions in templates
    // as long as nobody calls them: 
    void f(const TC<T> &crtc) const { crtc.g(); } 
};

int main()
{
    TC<int> tc;
    const TC<int> &crtc = tc;
    // crtc.f(crtc); // <-- comment this in to get the error.
}
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62