views:

207

answers:

7

Given the following code:

class foo;

foo* instance = NULL;

class foo
{
public:
   explicit foo(int j)
    : i(j)
   {
      instance = this;
   }

   void inc()
   {
      ++i;
   }

private:
   int i;
};

Is the following using defined behavior?

const foo f(0);

int main()
{
   instance->inc();
}

I'm asking because I'm using a class registry, and as I don't directly modify f it would be nice to make it const, but then later on f is modified indirectly by the registry.

EDIT: By defined behavior I mean: Is the object placed into some special memory location which can only be written to once? Read-only memory is out of the question, at least until constexpr of C++1x. Constant primitive types for instance, are (often) placed into read-only memory, and doing a const_cast on it may result in undefined behavior, for instance:

int main()
{
    const int i = 42;
    const_cast<int&>(i) = 0; // UB
}
+1  A: 

This may be one of the rare cases where the not very known mutable keyword could be used:

mutable int i;

i can now be changed even if the object is const. It's used when logically the object doesn't change, but in reality it does.


For example:

class SomeClass
{
// ....
    void DoSomething() { mMutex.lock(); ...; }
    mutable Mutex mMutex;
}

In DoSomething() the object doesn't logically change and yet mMutex has to change in order to lock it. So it makes sense to make it mutable, otherwise no instance of SomeClass could be const (assuming you lock the muetx for every operation).

Andreas Bonini
`mutable` should be used for data members that may be changed but which don't violate the "constness" of the reference. This is the case for instance with temporary variables. There's no indication that this is the case in the OP's case.
shoosh
There is no indication that this isn't the case either. I explained (I hope) clearly when he should(n't) use it; if he chooses to abuse it then too bad for him. I do agree though that I shouldn't have said "This is one of the cases where... ", I'll change it to "This may be one.."
Andreas Bonini
`mutable` isn't needed as I already have a non-const pointer to the object. Revising question.
dalle
I think that avakars post (http://stackoverflow.com/questions/1934367/indirectly-calling-non-const-function-on-a-const-object/1934456#1934456) describes this better.
dalle
A: 

Why dont you make use of const cast ?

Any reason to make object as const eventhough its state is not constant?

Also make following change :

explicit foo(int j = 0)    : i(j)   

{    instance = this;   }
Ashish
`const_cast` isn't needed as I already have a non-const pointer to the object.
dalle
A: 

It's hard to tell the intent with these arbitrary names. If i is intended as just a use counter, and it isn't really considered part of the data, then it is perfectly appropriate to declare it as mutable int i; Then the const-ness of an instance is not violated when i is modified. On the other hand, if i is meaningful data in the space being modeled, then that would be a very bad thing to do.

Separately from that, though, your example is a bit of a mess for what you seem to be asking. foo* instance = NULL; is effectively (if confusingly) using a NULL as a numeric zero and initializing instance, which is not const; then you separately initialize f, which is const, but never reference it.

Joe Mabel
He's assigning to `instance` in the constructor.
avakar
A: 

Under GCC, at least, your constructor should be explicit foo(int j) with the word int.

However, it's perfectly fine to have two pointers to the same value, one const and the other not.

Chip Uni
Sorry about the missing int. Sorry for any added confusion.
dalle
A: 

Calling a non-const (by declaration) member function on a const object is not illegal per se. You can use whatever method you wish to work around the compiler restrictions: either an explicit const_cast or a trick with constructor as in your example.

However, the behavior is only defined as long as the member function you are calling does not make an attempt to actually physically modify the object (i.e. modify a non-mutable member of the constant object). Once it makes an attempt to perform a modification, the behavior becomes undefined. In your case, method inc modifies the object, meaning that in your example the behavior is undefined.

Just calling the method, again, is perfectly legal.

AndreyT
+2  A: 

Yes, it is undefined behavior, as per 7.1.5.1/4:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Note that object's lifetime begins when the constructor call has completed (3.8/1).

avakar
Thank you. These quotes from the standard were the ones I was looking for. So if I would mark `foo::i` as mutable it would be okay then.
dalle
The behavior would be defined then, but you'd be better off defining `foo instance(0);` and `foo const `.
avakar
+2  A: 

If you define a const instance of the object, then cast away the const-ness, and modify the contents of the object, you get undefined behavior.

From the sound of things, what you want is exactly the opposite: create a non-const instance of the object, then return a const pointer to that object to (most of) the clients, while the "owner" retains a non-const pointer to the object so it can modify members as it sees fit.

You'd typically manage a situation like this by defining the class with a private ctor, so most clients can't create objects of the type. The class will then declare the owner class as a friend, so it can use the private ctor and/or a static member function to create instances (or often only one instance) of the object. The owner class then passes out pointers (or references) to const objects for clients to use. You need neither a mutable member nor to cast away constness, because the owner, which has the "right" to modify the object, always has a non-const pointer (or, again, reference) to the object. Its clients receive only const pointers/references, preventing modification.

Jerry Coffin
Do this. Store non-const pointers in the registry, and have both const and non-const accessor functions.
Alan