views:

387

answers:

5

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.

A: 

Use const_cast<>() or make your instance const.

I'm guessing maybe you want to be sure the operator returns a const double? Maybe you should just provide the const version, not the other one.

Klaim
+2  A: 

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;
Todd Gardner
Hmm, that sort of contradicts the "use is simple"-paradigm which I wanted to have using that operator...
steigers
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.
Todd Gardner
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
That is what `const_cast<>` is for. Edit - and you shouldn't do anything with pointers.
Potatoswatter
No, it isn't. const_cast is for removing constness, not adding it.
Todd Gardner
Oops, `static_cast` is weaker and therefore better, but still better to avoid referencing+dereferencing.
Potatoswatter
Todd Gardner
@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.
James McNellis
@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.
Todd Gardner
I don't like *_cast at all. Always got away without using it so far.
steigers
+2  A: 

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
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.
steigers
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.
Todd Gardner
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
+1  A: 

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.

jpalecek
OK, advanced... will think about it :-)
steigers
A: 

You have different methods with different functionality, so give them different names. Then you don't need to have a const object just to call what you want.

You can still make operator() const call the alternative function in case you do happen have a const object. But the alternative functionality should be put in a function with a descriptive name.

As for getting a const handle to an object, use static_cast< const Matrix & >( A ).

Potatoswatter