C++ Pitfall: const objects do not behave constantly.

K

Kira Yamato

It is erroneous to think that const objects will have constant behaviors too.

Consider the following snip of code:

class Person
{
public:
Person();

string get_name() const
{
Randomizer dice; // Randomizer is some class that returns random strings.
return dice.getRandomString();
}

void set_name(const string &name);
};

void func(const Person &person)
{
// note that the object 'person' is declared const, so it is immutable.

// However, the following two lines can print different names,
// even though we are invoking const member function 'getName'.

// print one name here,
cout << person.get_name() << endl;

// but it could print a different name here.
cout << person.get_name() << endl;
}

Thus, object behavior is not guarantee to be constant for const object
even though its state is unchanged.

This also means that compiler cannot make optimizations with const object.

So to me, declaring an object 'const' is really just a mechanism to
allow a certain subset of the object's member functions to be called
(those that are declared 'const') and disallow calling the other member
functions, (those that are not declared 'const'). No other conditions
can be assumed.
 
V

Victor Bazarov

Kira said:
It is erroneous to think that const objects will have constant
behaviors too.

What is your definition of "constant behaviour"?
Consider the following snip of code:

class Person
{
public:
Person();

string get_name() const
{
Randomizer dice; // Randomizer is some class that returns random
strings. return dice.getRandomString();
}

void set_name(const string &name);
};

void func(const Person &person)
{
// note that the object 'person' is declared const, so it is
immutable.
// However, the following two lines can print different names,
// even though we are invoking const member function 'getName'.

// print one name here,
cout << person.get_name() << endl;

// but it could print a different name here.
cout << person.get_name() << endl;
}

Thus, object behavior is not guarantee to be constant for const object
even though its state is unchanged.

Is it possible that you think somebody can confuse immutability with
repeatability?
This also means that compiler cannot make optimizations with const
object.

How do you make that leap? And what optimizations are we talking
here?
So to me, declaring an object 'const' is really just a mechanism to
allow a certain subset of the object's member functions to be called
(those that are declared 'const') and disallow calling the other
member functions, (those that are not declared 'const'). No other
conditions can be assumed.

That's pretty much the actual defintion of 'const'. Have you been
reading some book or article that says otherwise? Then please quote.

V
 
K

Kira Yamato

What is your definition of "constant behaviour"?

I was thinking of it as a state machine. If the state remains the
same, then equal inputs should produce equal outputs.

In my example, I was expecting the two instances of invocations of
get_name() to return the same thing.
Is it possible that you think somebody can confuse immutability with
repeatability?

Apparently so. I was thinking what the *intented* use of the keyword
'const' is. I thought it was intented to be so that things declared
'const' should yield repeatable results.
How do you make that leap? And what optimizations are we talking
here?

For example, if indeed it were treated like a state machine (so that
unchanged states should yield unchanged responses from 'const'
functions), then the compiler can remove the 2nd invocation of
get_name() and just use the result from the first invocation.
That's pretty much the actual defintion of 'const'. Have you been
reading some book or article that says otherwise? Then please quote.

Nowhere says that. I'm learning C++ mostly by examples from other
people's code. So, I'm mostly just guessing what each part of the
language is doing, and learning by trials and errors.

So, I suppose in this case coining this mechanism by the keyword
'const' is a poor choice, even though that is where it finds most
applications in. However, keywords should be chosen for what it
actually does instead of what it is intended to do. In the case of
'const', these two things are clearly not the same thing.
 
G

Guest

It is erroneous to think that const objects will have constant behaviors too.

Consider the following snip of code:

Please consider replacing tabs with a few spaces when posting.
class Person
{
public:
Person();

string get_name() const
{
Randomizer dice; // Randomizer is some class that returns random strings.
return dice.getRandomString();
}

void set_name(const string &name);
};

void func(const Person &person)
{
// note that the object 'person' is declared const, so it is immutable.

// However, the following two lines can print different names,
// even though we are invoking const member function 'getName'.

// print one name here,
cout << person.get_name() << endl;

// but it could print a different name here.
cout << person.get_name() << endl;
}

Thus, object behavior is not guarantee to be constant for const object
even though its state is unchanged.

This also means that compiler cannot make optimizations with const object.

So to me, declaring an object 'const' is really just a mechanism to
allow a certain subset of the object's member functions to be called
(those that are declared 'const') and disallow calling the other member
functions, (those that are not declared 'const'). No other conditions
can be assumed.

I would hardly call this a pitfall, and I have yet to meat anyone with a
bit of OO programming skill who fail to grasp the concept of constant
objects (namely that their state are constant/unchangeable). Personally
I think that the const concept is a really powerful one, especially when
parring arguments to functions. As an example when I as a programmer use
a third party API I know that I can safely pass any string I have to a
function taking a const string reference as parameter, since the string
will not be changed by the function. Without const I would not have that
guarantee and I would have to make a copy and pass that to be safe.
 
V

Victor Bazarov

Kira said:
I was thinking of it as a state machine. If the state remains the
same, then equal inputs should produce equal outputs.

In my example, I was expecting the two instances of invocations of
get_name() to return the same thing.

I am not sure what the basis of this is. Objects (in C++) are not
necessarily state machines. They are just objects. Anything that
is declared 'const' does not change. However, there is no guarantee
that it would not produce different output for the same input, those
things are totally orthogonal. Just like you've shown.
[..]
This also means that compiler cannot make optimizations with const
object.

How do you make that leap? And what optimizations are we talking
here?

For example, if indeed it were treated like a state machine (so that
unchanged states should yield unchanged responses from 'const'
functions), then the compiler can remove the 2nd invocation of
get_name() and just use the result from the first invocation.

Yes, and if the grandmother had b***s, she would be the grandfather.
Sorry for the rude analogy, but that's what you seem to have here --
you make up some dependency and then try to draw some conclusions
from it. And you seem surprised (disclaimer: I may have simply
misinterpreted your reaction).
Nowhere says that. I'm learning C++ mostly by examples from other
people's code. So, I'm mostly just guessing what each part of the
language is doing, and learning by trials and errors.

That's a bad way to learn a programming language. Trust me.
So, I suppose in this case coining this mechanism by the keyword

Coining *what* mechanism?
'const' is a poor choice, even though that is where it finds most
applications in. However, keywords should be chosen for what it
actually does instead of what it is intended to do. In the case of
'const', these two things are clearly not the same thing.

Here is another pitfall for you:

class HasOther {
OtherObject *ptr;
public:
HasOther(OtherObject *p) : ptr(new OtherObject) {}
void Gotcha(OtherObject const& value) const
{
if (ptr) *ptr = value;
}
};

In the 'Gotcha' function, which is declared 'const', the 'HasOther'
without _any_problem_ changes the value of the object to which it
refers, even though the 'HasOther' is constant. It has nothing to
do with behavour, but /some/ people would actually thing that the
value of the object to which 'ptr' points should also be constant.
It isn't.

Beware of the results you get from *expecting* something instead of
*learning* how it actually is.

V
 
K

Kira Yamato

Please consider replacing tabs with a few spaces when posting.


I would hardly call this a pitfall, and I have yet to meat anyone with a
bit of OO programming skill who fail to grasp the concept of constant
objects (namely that their state are constant/unchangeable).

What is the state of an object? A state of an object is supposed to be
a set of data that determines the behavior of an object. In another
word, knowing the state of an object would dictate what its functions
do given some inputs. Otherwise, what is the point of knowing an
object's state?

So, if you say that declaring an object 'const' should keep its state
constant, then its member functions should yield the same output for
the same input since the object remains in the same state!

However, this is not the case as shown in the example.

This is the pitfall.

Rigorously, all 'const' really does is that it is a switch to disable
the invocation of certain member functions --- those that are not
declared 'const'.
Personally
I think that the const concept is a really powerful one, especially when
parring arguments to functions. As an example when I as a programmer use
a third party API I know that I can safely pass any string I have to a
function taking a const string reference as parameter, since the string
will not be changed by the function. Without const I would not have that
guarantee and I would have to make a copy and pass that to be safe.

I do agree that the idea of guaranteeing an object's state be constant
is a powerful one. I'm just saying the actual use of 'const' is not
that. In essence, it is just an agreement of the API that it will not
invoke certain member functions (those that are not declared 'const')
of the object passed to it.

Rather or not the object declared 'const' will really keep its state
constant is depended upon what the class actually implements. In my
example, it does not do that, even though I've used 'const'.
 
P

Pete Becker

Rigorously, all 'const' really does is that it is a switch to disable
the invocation of certain member functions --- those that are not
declared 'const'.

It also prevents modification of data members that aren't marked
"mutable". Those data members are the object's state.
 
A

Anand Hariharan

(...)

So, I suppose in this case coining this mechanism by the keyword
'const' is a poor choice, even though that is where it finds most
applications in. However, keywords should be chosen for what it
actually does instead of what it is intended to do. In the case of
'const', these two things are clearly not the same thing.

Stroustrup had initially chosen 'readonly' as the keyword. Later, he
decided to replace it with 'const'.

There is some (very little) background here:
http://www.research.att.com/~bs/bs_faq2.html#constplacement

Check out "Design and Evolution of C++" for more details.

- Anand
 
J

Jim Langston

Kira Yamato said:
It is erroneous to think that const objects will have constant behaviors
too.

Consider the following snip of code:

class Person
{
public:
Person();

string get_name() const
{
Randomizer dice; // Randomizer is some class that returns random strings.
return dice.getRandomString();
}

void set_name(const string &name);
};

void func(const Person &person)
{
// note that the object 'person' is declared const, so it is immutable.

// However, the following two lines can print different names,
// even though we are invoking const member function 'getName'.

// print one name here,
cout << person.get_name() << endl;

// but it could print a different name here.
cout << person.get_name() << endl;
}

Thus, object behavior is not guarantee to be constant for const object
even though its state is unchanged.

This also means that compiler cannot make optimizations with const object.

So to me, declaring an object 'const' is really just a mechanism to allow
a certain subset of the object's member functions to be called (those that
are declared 'const') and disallow calling the other member functions,
(those that are not declared 'const'). No other conditions can be
assumed.

I believe you are confusing the word "constant" with "consistant". The
const keyword says change any of the objects variables. The result from
get_name() is not a variable. It is a function and can do anything. Notice
that get_name is declared const, it does not change any of the objects
variables. The variables with in the object remain constant, they do not
change. Output, however, may not be consistant depending on what the
methods do.
 
M

Mark P

Kira said:
Rigorously, all 'const' really does is that it is a switch to disable
the invocation of certain member functions --- those that are not
declared 'const'.

Wrong. Or at least incomplete. const is not an arbitrary label that
can be applied to any member function. The compiler will not let you
declare a member function constant if it attempts to perform
non-constant actions, including modifying the class member variables.

This whole argument is really quite silly since, as Victor pointed out,
it all stems from your confusion over the difference between constant
and repeatable.
 
G

Guest

What is the state of an object? A state of an object is supposed to be
a set of data that determines the behavior of an object. In another
word, knowing the state of an object would dictate what its functions
do given some inputs. Otherwise, what is the point of knowing an
object's state?

So, if you say that declaring an object 'const' should keep its state
constant, then its member functions should yield the same output for
the same input since the object remains in the same state!

However, this is not the case as shown in the example.

You would be right provided that the results (return values and side
effects) of the member functions were totally dependent on the object's
state. In the example above this is not the case, and there is thus no
need to for the results to be the same. Consider the following:

#include <iostream>

struct Foo
{
int bar(int i) const
{
return i;
}
};

int main()
{
const Foo f;
std::cout << f.bar(1) << "\n";
std::cout << f.bar(2) << "\n";
}

By your reasoning both calls to bar() should return the same thing, but
that would be totally illogical.
 
H

Howard

What is the state of an object? A state of an object is supposed to be a
set of data that determines the behavior of an object. In another word,
knowing the state of an object would dictate what its functions do given
some inputs. Otherwise, what is the point of knowing an object's state?

So, if you say that declaring an object 'const' should keep its state
constant, then its member functions should yield the same output for the
same input since the object remains in the same state!

However, this is not the case as shown in the example.

Your code is not reporting anything about the Person object's state. And
its state has not changed, so it's not violating anything about its
const-ness.

Regardless of the C++ semantics, in what way do you think that reporting an
observation of an external phenomenon (the Randomizer-generated value)
somehow violates an object's const-ness?

But let's use your definition of const-ness for the moment. Your Randomizer
object is an *input* to the Person object! It's an external entity which
returns value unrelated to the current state of the object calling it.
(Like reading a file or the mouse or the keyboard.) So when you call the
function, you're calling it with different inputs, and are getting different
outputs. Again, no violation of const-ness.

Now, move that Randomizer object into the class as a member of Person, and
then you've got a different story. The getRandomString function changes
that object's state, which means it can't be a const function and which
means it won't compile when calling it from a const function in Person.


-H
 
K

Kira Yamato

You would be right provided that the results (return values and side
effects) of the member functions were totally dependent on the object's
state. In the example above this is not the case, and there is thus no
need to for the results to be the same. Consider the following:

Yes, this is a good point. And this is what I'm saying too. The idea
of keeping the state of an object constant is not guarantee by 'const'
alone.
#include <iostream>

struct Foo
{
int bar(int i) const
{
return i;
}
};

int main()
{
const Foo f;
std::cout << f.bar(1) << "\n";
std::cout << f.bar(2) << "\n";
}

By your reasoning both calls to bar() should return the same thing, but
that would be totally illogical.

Actually it is logical here. The object remains same state here. So,
everytime I invoke with 1 I get back 1; and everytime I invoke 2 I get
back 2 here. The singleton state of this object dictates that the
function bar be an identity function on its input.
 
K

Kira Yamato

I believe you are confusing the word "constant" with "consistant". The
const keyword says change any of the objects variables. The result from
get_name() is not a variable. It is a function and can do anything. Notice
that get_name is declared const, it does not change any of the objects
variables. The variables with in the object remain constant, they do not
change. Output, however, may not be consistant depending on what the
methods do.

I know. And hence I named this a pitfall.

However, I'm starting to get the sense that this is a pitfall for
newbies only. Seems like everyone else has no problem with it except
me. :)
 
K

Kira Yamato

I am not sure what the basis of this is. Objects (in C++) are not
necessarily state machines. They are just objects. Anything that
is declared 'const' does not change. However, there is no guarantee
that it would not produce different output for the same input, those
things are totally orthogonal. Just like you've shown.

Right. This is the lesson I learned here.
[..]
This also means that compiler cannot make optimizations with const
object.

How do you make that leap? And what optimizations are we talking
here?

For example, if indeed it were treated like a state machine (so that
unchanged states should yield unchanged responses from 'const'
functions), then the compiler can remove the 2nd invocation of
get_name() and just use the result from the first invocation.

Yes, and if the grandmother had b***s, she would be the grandfather.
Sorry for the rude analogy, but that's what you seem to have here --
you make up some dependency and then try to draw some conclusions
from it. And you seem surprised (disclaimer: I may have simply
misinterpreted your reaction).

lol. I don't know where that came from, but I had a good chuckle out of it.
That's a bad way to learn a programming language. Trust me.


Coining *what* mechanism?


Here is another pitfall for you:

class HasOther {
OtherObject *ptr;
public:
HasOther(OtherObject *p) : ptr(new OtherObject) {}
void Gotcha(OtherObject const& value) const
{
if (ptr) *ptr = value;
}
};

In the 'Gotcha' function, which is declared 'const', the 'HasOther'
without _any_problem_ changes the value of the object to which it
refers, even though the 'HasOther' is constant. It has nothing to
do with behavour, but /some/ people would actually thing that the
value of the object to which 'ptr' points should also be constant.
It isn't.

Yea. This could be classified as an example of bad design. Clearly,
part of the state of the object is placed outside the object in another
external object. In this way, it bypasses the mechanism 'const'.
Beware of the results you get from *expecting* something instead of
*learning* how it actually is.

V

A very good point to keep in mind when programming. What we code is
not always what we intended.
 
T

Tim H

What is the state of an object? A state of an object is supposed to be
a set of data that determines the behavior of an object. In another
word, knowing the state of an object would dictate what its functions
do given some inputs. Otherwise, what is the point of knowing an
object's state?

So, if you say that declaring an object 'const' should keep its state
constant, then its member functions should yield the same output for
the same input since the object remains in the same state!

YOU are the one that declared the get_name() method as const. It is
literally bitwise const, but not really stateful const. If that
matters to your app - DO NOT DECLARE IT AS CONST.
 
G

Guest

Yes, this is a good point. And this is what I'm saying too. The idea
of keeping the state of an object constant is not guarantee by 'const'
alone.

It is not? Exactly which state can be changed in a function marked as const?
Actually it is logical here. The object remains same state here. So,
everytime I invoke with 1 I get back 1; and everytime I invoke 2 I get
back 2 here. The singleton state of this object dictates that the
function bar be an identity function on its input.

I can actually not see any difference between my example and the one you
gave, I have just moved the source of the randomness from inside the
function to outside. When it comes to the object's state they are equal
(in that the function's results do not depend on the state of the object).
 
G

Guest

I am not sure what the basis of this is. Objects (in C++) are not
necessarily state machines. They are just objects. Anything that
is declared 'const' does not change. However, there is no guarantee
that it would not produce different output for the same input, those
things are totally orthogonal. Just like you've shown.

Right. This is the lesson I learned here.
[..]
This also means that compiler cannot make optimizations with const
object.

How do you make that leap? And what optimizations are we talking
here?

For example, if indeed it were treated like a state machine (so that
unchanged states should yield unchanged responses from 'const'
functions), then the compiler can remove the 2nd invocation of
get_name() and just use the result from the first invocation.

Yes, and if the grandmother had b***s, she would be the grandfather.
Sorry for the rude analogy, but that's what you seem to have here --
you make up some dependency and then try to draw some conclusions
from it. And you seem surprised (disclaimer: I may have simply
misinterpreted your reaction).

lol. I don't know where that came from, but I had a good chuckle out of it.
That's a bad way to learn a programming language. Trust me.


Coining *what* mechanism?


Here is another pitfall for you:

class HasOther {
OtherObject *ptr;
public:
HasOther(OtherObject *p) : ptr(new OtherObject) {}
void Gotcha(OtherObject const& value) const
{
if (ptr) *ptr = value;
}
};

In the 'Gotcha' function, which is declared 'const', the 'HasOther'
without _any_problem_ changes the value of the object to which it
refers, even though the 'HasOther' is constant. It has nothing to
do with behavour, but /some/ people would actually thing that the
value of the object to which 'ptr' points should also be constant.
It isn't.

Yea. This could be classified as an example of bad design. Clearly,
part of the state of the object is placed outside the object in another
external object. In this way, it bypasses the mechanism 'const'.

No, one again you confuse what the state of the object is. The state of
the HasOther object is that it knows of another object of type
OtherObject. Changing the state of the OtherObject does not affect the
state of the HasOther object, just the state of OtherObject.

Think of it like this, if I have a fried and that friend changes his/her
hair-colour it does not mean that I have changed in any way. The
friendship is the pointer above, it creates a relation between me and my
friend. It does not however make him/her a part of me.
 
G

Guest

I know. And hence I named this a pitfall.

However, I'm starting to get the sense that this is a pitfall for
newbies only. Seems like everyone else has no problem with it except
me. :)

I would suspect that this stems from the fact that you have not had any
formal teaching or even read a book. Normally const is introduced on
simple variables first and then the concept is extended to classes. I
would suggest that you get yourself a good book and start reading that.
Many recommend Accelerated C++ by Koenig & Moe, Thinking in C++ by Bruce
Eckel is available for free on the net (just search for it).
 
J

James Kanze

What is your definition of "constant behaviour"?
[/QUOTE]
I was thinking of it as a state machine. If the state remains the
same, then equal inputs should produce equal outputs.

Why?

When the output is defined as being independant of the state,
why would you expect that it depend on the state? If there's an
error in your code, it's making the function you called a
member, since the function has nothing to do with the class.
But that's neither here nor there: one could easily imagine a
"valueAtTime" function which returned a compound value
(formatted string, struct, whatever) which contained some
internal value and the current time. Such a function should be
const because it doesn't change the observable state of the
object. And of course, the current time isn't part of the
observable state of the object, even if it is part of the return
value of some member function.
In my example, I was expecting the two instances of
invocations of get_name() to return the same thing.

Again, why?

[...]
Apparently so. I was thinking what the *intented* use of the
keyword 'const' is. I thought it was intented to be so that
things declared 'const' should yield repeatable results.
For example, if indeed it were treated like a state machine
(so that unchanged states should yield unchanged responses
from 'const' functions),

The only "state machine" in this sense is the complete computer.
then the compiler can remove the 2nd invocation of get_name()
and just use the result from the first invocation.

In other words, that the function is pure. Pure is a lot
stronger than const.
So, I suppose in this case coining this mechanism by the
keyword 'const' is a poor choice, even though that is where it
finds most applications in. However, keywords should be
chosen for what it actually does instead of what it is
intended to do. In the case of 'const', these two things are
clearly not the same thing.

There is a potential problem with const, in that the language
specifies bitwise const, where as the usual use is logical
const. But the keyword const suggests (in my mind, at least)
constant, not pure. A const function does not change the
(visible) state of the object it is called on. Which seems to
be the usual way it is used, as well. You seem to be confusing
const with pure.
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top