21

I've got a const method in my class, which cannot be changed to non-const. In this method, I need to call a non-const method but the compiler doesn't let me do that.

Is there any way around it? Here is a simplified sample of my code:

int SomeClass::someMethod() const {
    QColor saveColor = color();
    setColor(QColor(255,255,255)); // Calling non-const method

    // ....

    setColor(saveColor); // restore color

    return 1;
}
laurent
  • 88,262
  • 77
  • 290
  • 428
  • 4
    Wouldn't it be against the cardinal rule of being a const function ? – DumbCoder Nov 30 '11 at 11:45
  • 3
    @DumbCoder, from the outside, it is a const function in that nothing visible to the client has been changed. – laurent Nov 30 '11 at 11:48
  • A non-const method implies that it is changing some data in your object. If `someMethod()` calls a non-const method, then it is indirectly changing data. So really, `someMethod()` simply shouldn't be `const`. – Chris Parton Nov 30 '11 at 11:50
  • 4
    The `mutable` keyword allows changing data in a const method, yet nobody would suggest to stop using mutable and change the method to non-const. As a programmer, I know that I'm setting a color, then restoring this color. I know the data hasn't been changed, so I'm looking for a way to indicate that to the compiler. – laurent Nov 30 '11 at 11:56
  • 1
    @Laurent: what you say is true, but if someone creates a `const` instance of `SomeClass`, then it is defined behavior to modify its `mutable` members, but UB to modify its non-mutable members. Therefore, there is an important difference between the situation you actually have and the situation you compare it to. The effect of marking `someMethod` const is to allow callers to call it on const objects, as well as calling it on non-const objects via references-to-const, and you can't separate the two. Casting away const relies on the caller only using the function on non-const objects. – Steve Jessop Nov 30 '11 at 12:50
  • 3
    A potential solution is to alter the code in `// ....` so that it doesn't need to use the object's "current color". Add versions of all the functions you use, that take a `QColor` parameter instead. That relies on being able to change the interfaces used in the hidden code, of course. It may also have different behavior - in your code, if `// ....` throws then the object's color has been changed, whereas you'll lose that effect in my suggestion. – Steve Jessop Nov 30 '11 at 12:59
  • 1
    Another possibility: is the object `SomeClass` copyable? You could do `SomeClass sc = *this; sc.setColor(White);` then use `sc` in place of `*this` throughout `// ....`. – Steve Jessop Nov 30 '11 at 13:03
  • 1
    +1: It's a well-formed question. Some people are probably downvoting you because they think you're trying to do the *Wrong Thing*, but IMO that's not the right reason to downvote. In any case, I'm not upvoting to offset the downvotes -- I'm upvoting because it's a well-formed question. – John Dibling Nov 30 '11 at 18:19
  • @Laurent "_from the outside, it is a const function in that nothing visible to the client has been changed_" from the outside, this function cannot be called on a `const` object – curiousguy Dec 01 '11 at 13:38
  • @curiousguy, yes that was my problem, I had a const object trying to call this function, which is why my code wasn't compiling. – laurent Dec 01 '11 at 13:41

4 Answers4

22

You could use const_cast on this pointer,

int SomeClass::someMethod() const {
    const_cast<SomeClass*>( this )->setColor(...);// Calling non-const method
    //whatever
}

but if you do that for an object that was originally declared const you run into undefined behavior.

So this:

SomeClass object;
object.someMethod();

is okay, but this:

const SomeClass object;
object.someMethod();

yields undefined behavior.

The real solution is that your const function should not be const in the first place.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 1
    I suppose one could use a factory and private constructors, to ensure that there are no `const` instances of `SomeClass`. But unless that's already there, it's a much bigger change to the class interface than the changes that the questioner is forbidden to make, so probably not much help. – Steve Jessop Nov 30 '11 at 12:56
  • 1
    This is a better answer than the marked one as it explains when you can get undefined behaviour. Sometimes you have no choice but to cast away const. – Damian Dixon Aug 28 '20 at 07:59
13

One of the challenges of doing const-correctness is you can't do it halfway. It's either all or nothing. If you try to do it halfway, you end up in a tough spot like you are here. You end up with a nice const-correct class being used by some crazy old, typically legacy (or written by an old curmudgeon) code that isn't const-correct and it just doesn't work. You're left wondering if const-correctness is worth all the trouble.

I need to call a non-const method [from a const method]

You can't -- not directly. Nor should you. However, there is an alternative...

Obviously you can't call a non-const method from a const method. Otherwise, const would have no meaning when applied to member functions.

A const member function can change member variables marked mutable, but you've indicated that this is not possible in your case.

You could attempt to cast away constness by doing something like SomeClass* me = const_cast<SomeClass*>(this); but A) This will typically result in UB, or 2) It violates the whole idea of const-correctness.

One thing you could do, if what you're really trying to accomplish would support this, is to create a non-const proxy object, and do nonconst-y stuff with that. To wit:

#include <iostream>
#include <string>
using namespace std;

class Gizmo
{
public:
    Gizmo() : n_(42) {};
    void Foo() const;
    void Bar() { cout << "Bar() : " << n_ << "\n"; }
    void SetN(int n) { n_ = n; };
    int GetN() const { return n_; }
private:
    int n_;
};

void Gizmo::Foo() const
{
    // we want to do non-const'y things, so create a proxy...
    Gizmo proxy(*this);
    int save_n = proxy.GetN();
    proxy.SetN(save_n + 1);
    proxy.Bar();
    proxy.SetN(save_n);
}

int main()
{
    Gizmo gizmo;
    gizmo.Foo();
}
xashru
  • 3,400
  • 2
  • 17
  • 30
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • How is it useful? I'm confused. The proxy here is not a proxy, for it doesn't represent the original object. It is just a local object with no extraordinary power. Working with proxy is not equivalent to working with the original object. – Nawaz Dec 01 '11 at 06:24
  • 2
    @nawaz: if it helps op get his work done in a standard-conformant way, its useful. – John Dibling Dec 01 '11 at 13:59
  • 2
    Sometimes a class is written without const markers because C++ devs are lazy, so being able to say "this *really* is const even though it's not marked as such" is a thing sometimes. – zostay Jun 29 '19 at 00:36
10

If you require to change some internal state inside a const-method you can also declare the affected state mutable:

class Foo {
public:
    void doStuff() const { bar = 5; }
private:
    mutable int bar;
};

This is intended for cases where you have stuff like mutexes as members of your class. Acquiring and releasing a mutex does not affect client-visible state, but is technically forbidden in a const-method. The solution is to mark the mutex mutable. Your case looks similar, although I think your class requires some refactoring for this solution to be applicable.

Also, you might want to read this answer to see how you can make this temporary state-change exception-safe using RAII.

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
6

How to call a non-const method from a const method?

You should not. You might run into undefined behaviour if you cast away the const-ness of this, using const_cast. The usage ofconst_cast will shut the compiler's mouth up, but that isn't a solution. If you need to do, then it means the const function should not be const in the first place. Make it non-const.

Or, you should do something else, which would not require you to call non-const function from const function. Like, don't call setColor function? Like, split the const function into more than one functions (if you can do that)? Or something else?

In your particular case, if setColor only sets some member variable, say m_color, then you can declare it mutable:

 mutable QColor m_color;

and then set it in your const function, without calling setColor function, and without doing const_cast.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    That's not an answer to the question as I just wrote I cannot change it to non-const. – laurent Nov 30 '11 at 11:51
  • 2
    @Laurent, potentially you could, if you could make the members touched in that function `mutable`? – Nim Nov 30 '11 at 11:54
  • 3
    If this is not an answer to your question, then your question is unanswerable. – tenfour Nov 30 '11 at 11:54
  • 1
    @tenfour, then vote to close it. If there's no answer, there's no point answering off-topic. – laurent Nov 30 '11 at 11:58
  • 1
    @Laurent: if it gets closed, how would know what is wrong with the question? Does closing *please* you more than *attempting* to your answer? – Nawaz Nov 30 '11 at 12:00
  • @Nawaz, my comment was applying to your very first post. Your current answer is very useful in explaining the problem and what to do about it. – laurent Dec 01 '11 at 06:01