Const operator overloading problems in C++

10,334

Solution 1

The overloading looks fine but you never call it on a const object. You can try this:

void foo(const Matrix& A) {
  cout << "A(1,1)=" << A(1,1) << endl;
}

Matrix A(10,10);
foo(A);

This gives you:

 - const-version was called - A(1,1)=0

Solution 2

The object you are calling the method on must be const, e.g.

cout << "A(2,2)=" << (*static_cast<const Matrix*>(&A))(2,2) << endl;

Solution 3

Generally, you can't call a const or non-const version of a function depending on what you do with the return value. If you want to emulate similar functionality, you can try returning some proxy that will switch the behaviour depending on what you do with it:

class Proxy
{
  Matrix& m;
  int x, y;
public:
  ...
// mutating operations
  operator double&() { check(); return m.index(x,y); }
  double& operator=(double d) { check(); return m.index(x,y)=d; }
// ... other mutating operations (+=, ...) analogously

// nonmutating ops
  operator double() { return m.const_index(x, y); }
  operator const double&() // ... same
};

Proxy Matrix::operator(int x, int y)
{
  return Proxy(*this, x, y);
}

Assuming check() is your check for legal mutation (could be integrated in index()) and index() and const_index() are methods in Matrix that give a reference or const reference to a particular place.

Share:
10,334
steigers
Author by

steigers

Updated on July 01, 2022

Comments

  • steigers
    steigers almost 2 years

    I'm having trouble with overloading operator() with a const version:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Matrix {
    public:
        Matrix(int m, int n) { 
            vector<double> tmp(m, 0.0);
            data.resize(n, tmp);
        }
        ~Matrix() { }
    
    
        const double & operator()(int ii, int jj) const {
            cout << " - const-version was called - ";
            return data[ii][jj];
        }
    
        double & operator()(int ii, int jj) {
            cout << " - NONconst-version was called - ";
            if (ii!=1) {
                throw "Error: you may only alter the first row of the matrix.";
            }
            return data[ii][jj];
         }
    
    
    protected:  
        vector< vector<double> > data;
    };
    
    int main() {
    try {
        Matrix A(10,10);
        A(1,1) = 8.8;
        cout << "A(1,1)=" << A(1,1) << endl;
        cout << "A(2,2)=" << A(2,2) << endl;
        double tmp = A(3,3);
    } catch (const char* c) { cout << c << endl; }
    }
    

    This gives me the following output:

    • NONconst-version was called - - NONconst-version was called - A(1,1)=8.8
    • NONconst-version was called - Error: you may only alter the first row of the matrix.

    How can I achieve that C++ call the const-version of operator()? I am using GCC 4.4.0.

    • steigers
      steigers about 14 years
      I'd like to be able and modify my matrix entries, but only some.
  • steigers
    steigers about 14 years
    Hmm, that sort of contradicts the "use is simple"-paradigm which I wanted to have using that operator...
  • Todd Gardner
    Todd Gardner about 14 years
    Well, this should almost never be necessary; your const and non-const versions should be very close in behavior, with the possible exception of a non-const version possibly resizing the container for you. If you are doing something so completely different in the two versions that you need to cast between them, consider making different methods instead.
  • steigers
    steigers about 14 years
    Thanks! It's unfortunate in my opinion that C++ doesn't call the const-methods "by default" even on non-const objects. I would put that on the wish list.
  • Todd Gardner
    Todd Gardner about 14 years
    If that were the case, when would the non-const methods be called? You could always remove the non-const overload, and it would call the const one even on non-const objects.
  • steigers
    steigers about 14 years
    The would be called when a non-const operation needs to be performed. Yes, I can remove the non-const overloaded method, but since I also sometimes want to assign something to a matrix element that reduces the functionality. Thanks for the advice.
  • steigers
    steigers about 14 years
    I wanted to have this for banded matrices, where I can call the non-const operator() only when I'm near the diagonal of the matrix (otherwise the elements are not stored and implicitly zero, so the operator needs to throw an error). I will do different methods now.
  • steigers
    steigers about 14 years
    OK, advanced... will think about it :-)
  • Potatoswatter
    Potatoswatter about 14 years
    That is what const_cast<> is for. Edit - and you shouldn't do anything with pointers.
  • Todd Gardner
    Todd Gardner about 14 years
    No, it isn't. const_cast is for removing constness, not adding it.
  • Potatoswatter
    Potatoswatter about 14 years
    Oops, static_cast is weaker and therefore better, but still better to avoid referencing+dereferencing.
  • Todd Gardner
    Todd Gardner about 14 years
    A good compiler would probably remove that; but the code would be clearer without pointers. I only used them because I couldn't remember if straight references would compile, while I knew the pointers would. If I would probably write this as implicit_cast<const Matrix&>(A)(2,2)
  • James McNellis
    James McNellis about 14 years
    @Todd: const_cast can be used both to add and to remove const and volatile qualifiers. It's just not generally used for adding the qualifiers since they can be added without using it. @Potatoswatter: Why do you think static_cast is preferable in this case? Doesn't const_cast more clearly represent the intent? (I guess I'm not sure why you think static_cast is "weaker;" the two casts are complementary in nature). @Todd again: I like the use of implicit_cast for this.
  • Todd Gardner
    Todd Gardner about 14 years
    @James - I disagree; const_cast's are more powerful than static_casts (or my more preferred one, currently-not-in-the-standard, implicit_cast), and should not be used a safe operation like adding constness. Seeing a const_cast in code means there is a large design flaw; using it to add constness severely waters down this meaning.
  • steigers
    steigers about 14 years
    I don't like *_cast at all. Always got away without using it so far.
  • doizuc
    doizuc about 10 years
    Actually, this doesn't compile : operator double() { return m.const_index(x, y); } operator const double&() Whereas this compile adding the const at the end of the second line: operator double() { return m.const_index(x, y); }; operator const double&() const;