mutable vs const_cast, which is better to modify field from constfunction?

Q

Qi

It's not rare to lazy initialize member field in getter functions which
are const function.

class SomeClass {
public:
MyObject * getMyObject() const {
if(! this->myObject) {
this->myObject = create the object;
}

return this->myObject;
}

private:
MyObject * myObject;
};

To be able to modify myObject in the getter function, we can
either,
1, Use mutable on myObject.
mutable MyObject * myObject;
or
2, Use const_cast to cast away constness from "this".
const_cast<SomeClass *>(this)->myObject = create the object;

Both methods have their own pros and cons, that's why I can't
decide which is better.

Method 1 (mutable),
Pros:
It's quite nature. Since myObject can be modified by a
const function, it's natural that it's mutable.

Cons:
A, "mutable" makes myObject to be changable to all const
functions. This is the problem. It should not be able to be
modified by any const functions except the lazy initialization
(the getter).
B, Any one reading the code may misunderstand that myObject
is really mutable while it's not.

Method 2 (const_cast),
Pros:
The effect is only limited to the getter function, any other
const functions can't modify myObject.

Cons:
A, Casting a const "this" to non-const looks so ugly and not
natural.
B, And a const function being able to modify a non-mutable member
is not natural too.

My questions:
1, Which method do you prefer to implement lazy initialization?
2, I think both methods are quite standard C++ (03), right?
If not, please point out.


Thanks
 
N

Narinder

It's not rare to lazy initialize member field in getter functions which
are const function.

class SomeClass {
public:
        MyObject * getMyObject() const {
                if(! this->myObject) {
                        this->myObject = createthe object;
                }

                return this->myObject;
        }

private:
        MyObject * myObject;

};

To be able to modify myObject in the getter function, we can
either,
1, Use mutable on myObject.
mutable MyObject * myObject;
or
2, Use const_cast to cast away constness from "this".
const_cast<SomeClass *>(this)->myObject = create the object;

Both methods have their own pros and cons, that's why I can't
decide which is better.

Method 1 (mutable),
Pros:
It's quite nature. Since myObject can be modified by a
const function, it's natural that it's mutable.

Cons:
A, "mutable" makes myObject to be changable to all const
functions. This is the problem. It should not be able to be
modified by any const functions except the lazy initialization
(the getter).
B, Any one reading the code may misunderstand that myObject
is really mutable while it's not.

Method 2 (const_cast),
Pros:
The effect is only limited to the getter function, any other
const functions can't modify myObject.

Cons:
A, Casting a const "this" to non-const looks so ugly and not
natural.
B, And a const function being able to modify a non-mutable member
is not natural too.

My questions:
1, Which method do you prefer to implement lazy initialization?
2, I think both methods are quite standard C++ (03), right?
If not, please point out.

Thanks

Deviating as little as possible from your code and addressing ONLY
your question, here is my suggestion:

-------------------------------------------
struct MyObject{};

struct SomeClass
{
SomeClass():myObject(0){}

const MyObject * getMyObject()const
{
return getMyObject_impl();
}

MyObject * getMyObject()
{
return getMyObject_impl();
}

private:

const MyObject * getMyObject_impl()const
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}

MyObject * getMyObject_impl()
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}

mutable MyObject * myObject;
};

-------------------------------------------------


So as long as all your other member functions access myObject via the
getMyObject_impl methods it should be ok.


Rgds
N
 
I

Ian Collins

It's not rare to lazy initialize member field in getter functions which
are const function.

class SomeClass {
public:
MyObject * getMyObject() const {
if(! this->myObject) {
this->myObject = create the object;
}

return this->myObject;
}

You don't need all those "this->".
private:
MyObject * myObject;
};

To be able to modify myObject in the getter function, we can
either,
1, Use mutable on myObject.
mutable MyObject * myObject;
or
2, Use const_cast to cast away constness from "this".
const_cast<SomeClass *>(this)->myObject = create the object;

Both methods have their own pros and cons, that's why I can't
decide which is better.

How about:

struct SomeClass
{
SomeClass() {}

MyObject* getMyObject() const
{
static MyObject* myObject;

if(!myObject)
{
myObject = new MyObject;
}

return myObject;
}
};
 
Q

Qi

struct MyObject{};

struct SomeClass
{
SomeClass():myObject(0){}

const MyObject * getMyObject()const
{
return getMyObject_impl();
}

MyObject * getMyObject()
{
return getMyObject_impl();
}

private:

const MyObject * getMyObject_impl()const
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}

MyObject * getMyObject_impl()
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}

mutable MyObject * myObject;
};

-------------------------------------------------


So as long as all your other member functions access myObject via the
getMyObject_impl methods it should be ok.

First, sorry that I forgot the "const" in the return of the getter.
I did mean const MyObject * getMyObject()const.

Back to my question, seems you are suggesting to use "mutable", right?
 
Q

Qi

You don't need all those "this->".

That's my personal style. I like those "this->"
because it makes members more obvious, if not considering
ADL. I used that style in all my C++, Java, Action Script
code.

Hope we stop here because that topic is quite subjective
and off topic. :)

How about:

struct SomeClass
{
SomeClass() {}

MyObject* getMyObject() const
{
static MyObject* myObject;

if(!myObject)
{
myObject = new MyObject;
}

return myObject;
}
};

That's another method.
But I do want myObject be member data so other functions
may check "myObject == NULL" when initialization is not
required.
 
I

Ian Collins

That's another method.
But I do want myObject be member data so other functions
may check "myObject == NULL" when initialization is not
required.

struct SomeClass
{
SomeClass() {}

MyObject* getMyObject( bool initialise = false ) const
{
static MyObject* myObject = NULL;

if( !myObject && initialise )
{
myObject = new MyObject;
}

return myObject;
}
};
 
S

SG

It's not rare to lazy initialize member field in getter functions
which are const function.

That sounds like a job for the mutable keyword. If the const function
does not alter the observable state it's still "logical constness".
Some private mutable data member does not hurt in this case.
class SomeClass {
public:
        MyObject * getMyObject() const {
                if(! this->myObject) {
                        this->myObject = createthe object;
                }

                return this->myObject;
        }

private:
        MyObject * myObject;

};

To be able to modify myObject in the getter function, we can
either,
1, Use mutable on myObject.
mutable MyObject * myObject;

Yes. Do that.
or
2, Use const_cast to cast away constness from "this".
const_cast<SomeClass *>(this)->myObject = create the object;

Bad idea. This will lead to undefined behaviour in a case like this:

const SomeClass x;
x.getMyObject(); // BOOM! U.B.
Cons:
A, "mutable" makes myObject to be changable to all const
functions. This is the problem. It should not be able to be
modified by any const functions except the lazy initialization
(the getter).

It is not modified in terms of observable state. mutable allows you to
implement caching and things like that without altering the "logical
state" of the object. This is the purpose of mutable.

SG
 
N

Narinder

First, sorry that I forgot the "const" in the return of the getter.
I did mean const MyObject * getMyObject()const.

Back to my question, seems you are suggesting to use "mutable", right?
seems you are suggesting to use "mutable", right?
Yes I am, but at the same time if it's only ever accessed via the
private impl methods than effectively it's not mutable for your other
member functions. I would stay away from const_cast, I use it only in
debug specific code ( eg to make certain views available into const
data type to the debugger).
In the end you want your pointer to be mutable only for the
initialiser method, so, make it only mutable for the initialiser
method, which in my case are the private impl methods.
You could go one step further and encompass your pointer in a class
of it's own.

------------------------------------------
struct MyObject{};

struct MyObjectWrapper
{
MyObjectWrapper():myObject(0){}
const MyObject * getMyObject_impl()const
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}

MyObject * getMyObject_impl()
{
if(!myObject)
{
myObject = new MyObject();

}
return myObject;
}
private:
mutable MyObject * myObject;
};

struct SomeClass
{

const MyObject * getMyObject()const
{
return myObject.getMyObject_impl();
}

MyObject * getMyObject()
{
return myObject.getMyObject_impl();
}
private:

MyObjectWrapper myObject;

};
------------------------------------------
 
B

Balog Pal

Qi said:
It's not rare to lazy initialize member field in getter functions which
are const function.

Changing the state of an object from a const function is generally NOT FAIR.
We use mutable members to have elements that are not considered as part of
the state. Like memoize cache. That can be recreated from real state.

Your case is not clear where it belongs. If you have a special "empty"
state, the getter discovers, it can use it to answer the query, and there is
no need to alter anything.

If you do alter the state, it is better to be honest about it. And not do it
with either cast or mutable.

For really non-state members, prefer mutable, and make special
documentation/warning on that. In multithreaded world peope normally map
logical constness to physical one, and may be hit by race conditions.

Also, modifying an object that was originally created const is undefined
behavior.

Doing const_cast inside class may still be fair game in circumstances you
are sure you had a non-const instance, and only gained and excess const
somehow -- or you're forcing some overloads.
 
Q

Qi

It's not rare to lazy initialize member field in getter functions which
are const function.

Hi all,

Thanks you all so much for pointing out the Undefined Behavior to
modify a const object.
I didn't know that before.

So I will definitely keep away from const_cast<>(this) and go
mutable way.

For anyone has interesting in learning UB on modifying const object,
here is the quote from the standard 03,

7.1.5.1 p4,
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.
 
Q

Qi

class SomeClass
{
public:
const int* getMyInt() const
{
if(!myInt)
{
myInt = new int;
}
return myInt;
}

private:
int const * mutable myInt;
};

mutable int const * myInt;

Works
 
N

Narinder

On 2011-7-9 15:28, Ian Collins wrote:

That's my personal style. I like those "this->"
because it makes members more obvious, if not considering
ADL. I used that style in all my C++, Java, Action Script
code.

Hope we stop here because that topic is quite subjective
and off topic. :)

Your style of using "this->" is definitely a good style. It's a good
habit to form. Consider the following code:

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

template<class T>
void foo(T)
{
cout << "I am global foo\n";
}

template<class U>
struct base
{
template<class T>
static void foo(T)
{
cout << "I am static foo\n";
}
void foo(U t)
{
cout << "I am in base \n";
}
};

template<class T>
struct derived :public base<T>
{
void g()
{
foo(3.0);
}

void f()
{
this->foo(3.0);
}
};

int main()
{
derived<double> d;
d.f();
d.g();
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top