What is the "philosophical" meaning of const?

G

Guest

Hello,

I know the rules for const handling in C++, but I'd like to ask what is
the "right" way to use them, eg. when is it appropriate to make a member
function const?

This came across this question when I was thinking about implementation
of a class implementing some lazy data structure or cache. The C++ rules
allow among other possibilities making all member functions non-const,
or making all member functions const and all members mutable. These are
the extreme possibilities, but in cases I was thinking about, the
differences are more subtle. Imagine, for example, a lazy data
structure, like the union-find data structure with path compression.
This data structure maintains a partition of some set and has two major
operations:

1. union: for two representatives of subsets in the partition, alter the
data structure so the partition contains all subsets except the two,
which are replaced by their union.

2. find: for a member of the set, find a representative of the subset it
is in.

From this description, it may seem that union is non-const and find is
const. But if you look at the implementation, the find alters the data
structure. So is it const or not?

Is const more like "doesn't touch the inner state of the object" or "you
won't notice the state changed"?

What is the compilers' view on this? Do they use const to make some
optimizations? Is there any danger to use cast to remove the const
(which clearly violates the purpose of the const)?

Regards
Jiri Palecek
 
F

Frederick Gotham

=?UTF-8?B?SmnFmcOtIFBhbGXEjWVr?= posted:
Hello,

I know the rules for const handling in C++, but I'd like to ask what is
the "right" way to use them, eg. when is it appropriate to make a member
function const?


The rule of thumb I advocate is: Make things const whenever you can.

There are exceptions however:

(1) Functions which return by value

int Func();

is prefereable over:

int const Func();

(2) Casts which yield an R-value

(int)

is preferable over:

(int const)

The reason why I don't use const in these places is because it would be
redundant, because R-values can't be altered in anyway.

What is the compilers' view on this? Do they use const to make some
optimizations?


Yes, quite a lot.

Is there any danger to use cast to remove the const
(which clearly violates the purpose of the const)?


If you alter a const object, you invoke undefined behaviour.
 
M

Marcus Kwok

Ji?? Pale?ek said:
I know the rules for const handling in C++, but I'd like to ask what is
the "right" way to use them, eg. when is it appropriate to make a member
function const?
[snip]

Is const more like "doesn't touch the inner state of the object" or "you
won't notice the state changed"?

Have a look at the "Const correctness" section of the FAQ. In
particular, 18.10 says,

The trailing const on [a] member function means that the abstract
(client-visible) state of the object isn't going to change. This is
slightly different from promising that the "raw bits" of the object's
struct aren't going to change.

So, I would say that it would be more for logical constness ("you won't
notice the state changed").
What is the compilers' view on this? Do they use const to make some
optimizations? Is there any danger to use cast to remove the const
(which clearly violates the purpose of the const)?

They touch on these topics in the FAQ as well. Basically, prefer
mutable members to const_casts, but of course the specifics of your
program may require otherwise.
 
F

Frederick Gotham

Frederick Gotham posted:
The reason why I don't use const in these places is because it would be
redundant, because R-values can't be altered in anyway.


Unless of course we're dealing with user-defined types; but still, I don't
think I'd use const, e.g.:

#include <string>
using std::string;

void Func(string const&) {}

void Func(char const*) {}

int main()
{
Func( string("Hello") );
}

(Note that:

string("Hello")

is equivalent to:

(string)"Hello"

NOT:

(string const)"Hello"
 
K

Kai-Uwe Bux

Frederick said:
=?UTF-8?B?SmnFmcOtIFBhbGXEjWVr?= posted:



The rule of thumb I advocate is: Make things const whenever you can.

There are exceptions however:

(1) Functions which return by value

int Func();

is prefereable over:

int const Func();

(2) Casts which yield an R-value

(int)

is preferable over:

(int const)

The reason why I don't use const in these places is because it would be
redundant, because R-values can't be altered in anyway.

That is not entirely correct: one can alter r-values by calling non-const
member functions:

#include <iostream>

struct A {

int data;

A ( int i )
: data ( i )
{}

A & inc ( void ) {
++data;
return ( *this );
}

A & print ( std::eek:stream & ostr ) {
ostr << data << '\n';
return ( *this );
}

};

A func ( int i ) {
return ( A(i) );
}

int main ( void ) {
func(5).inc().print( std::cout );
}


Sometimes this can lead to surprises, in particular when it comes to
operator<< which sometimes is free standing and sometimes a member
function.


[snip]

Best

Kai-Uwe Bux
 
J

Jeremy Brown

Jir(í Palec(ek said:
Hello,

I know the rules for const handling in C++, but I'd like to ask what is
the "right" way to use them, eg. when is it appropriate to make a member
function const?

This came across this question when I was thinking about implementation
of a class implementing some lazy data structure or cache. The C++ rules
allow among other possibilities making all member functions non-const,
or making all member functions const and all members mutable. These are
the extreme possibilities, but in cases I was thinking about, the
differences are more subtle. Imagine, for example, a lazy data
structure, like the union-find data structure with path compression.
This data structure maintains a partition of some set and has two major
operations:

1. union: for two representatives of subsets in the partition, alter the
data structure so the partition contains all subsets except the two,
which are replaced by their union.

2. find: for a member of the set, find a representative of the subset it
is in.

From this description, it may seem that union is non-const and find is
const. But if you look at the implementation, the find alters the data
structure. So is it const or not?

Is const more like "doesn't touch the inner state of the object" or "you
won't notice the state changed"?

What is the compilers' view on this? Do they use const to make some
optimizations? Is there any danger to use cast to remove the const
(which clearly violates the purpose of the const)?

Regards
Jiri Palecek
Well.. basically there are two camps.. the bitwise const camp and the
conceptual const camp. Bitwise constness believes that a member function
is const iff no bits are modified inside the object. The Conceptual
const camp believes that a const member function may modify some of the
bits of an object as long as it is undetectable by the client.

The C++ compiler enforces bitwise constness. For those that believe in
conceptual constness there are ways to get around a const member
function that involves casting away constness inside the function which
is always safe, or defining a variable mutable
 
J

jpalecek

Hi
Yes, quite a lot.

Could you be more specific? I think that the only factr you can infer
from that const is idempotency if you don't take the return value
(Imagine rand(). It can always open a file /dev/random, read and
close it, not modify any members of a class it is in and thus
satisfy const, yet return different values -- but, for example, in
gcc, const + __attribute__((pure)) CAN mean something)

Regards
Jiri Palecek
 
J

jpalecek

Have a look at the "Const correctness" section of the FAQ. In
particular, 18.10 says,

The trailing const on [a] member function means that the abstract
(client-visible) state of the object isn't going to change. This is
slightly different from promising that the "raw bits" of the object's
struct aren't going to change.

So, I would say that it would be more for logical constness ("you won't
notice the state changed").

Yes, but does the next sentence

C++ compilers aren't allowed to take the "bitwise" interpretation
unless they can solve the aliasing problem, which normally can't be
solved (i.e., a non-const alias could exist which could modify the
state of the object)

imply (very fuzzily though) that compiler can use the "bitwise" rule if
they can
solve the aliasing issue (eg. by __restricted, or by simply not having
any
other pointer around)?
They touch on these topics in the FAQ as well. Basically, prefer
mutable members to const_casts, but of course the specifics of your
program may require otherwise.

I do not quite understand section 18.14. What is "flushing the register
cache?" Do they mean the data cache of the CPU, or writing the state
from registers to memory, which can IMHO be delayed only if the
previous non-const call was inlined and if the compiler does it like
this, he has to deal with it. The fact that it hasn't been written to
memory, doesn't imply that it hasn't changed. If memory writeback
is required, volatile and some memory barrier trickery is requred IMHO.

Regards
Jiri Palecek
 
M

Marcus Kwok

Jiri,
Please do not snip the attribution line when quoting. I have added it
back in.

Marcus said:
Have a look at the "Const correctness" section of the FAQ. In
particular, 18.10 says,

The trailing const on [a] member function means that the abstract
(client-visible) state of the object isn't going to change. This is
slightly different from promising that the "raw bits" of the object's
struct aren't going to change.

So, I would say that it would be more for logical constness ("you won't
notice the state changed").

Yes, but does the next sentence

C++ compilers aren't allowed to take the "bitwise" interpretation
unless they can solve the aliasing problem, which normally can't be
solved (i.e., a non-const alias could exist which could modify the
state of the object)

imply (very fuzzily though) that compiler can use the "bitwise" rule if
they can
solve the aliasing issue (eg. by __restricted, or by simply not having
any
other pointer around)?

I do not know the semantics of "restricted" (I believe it was added to
C99, but not to Standard C++ yet), but my guess is that if the aliasing
problem has been solved, and there are no mutable members, and no
const_cast trickery, then it may be allowed to use the "bitwise"
interpretation, but this is just a guess.
I do not quite understand section 18.14. What is "flushing the register
cache?"

I take it to mean that a processor may choose to perform data
manipulations in registers, and at a later point in time write these
changes back to memory. Therefore "flushing the register cache" would
mean clearing the registers and loading them with "fresh" values from
memory.

I think what it is saying in 18.14 is that even if you call a const
member function on a "const" object, the const may only apply in this
context and there may be another context in which the object is not
const. For example:

class A;
f(const A& a);

A a;
f(a);
a.mutable_function();
f(a);

Inside f(), a is a const object. However, a non-const object can be
passed in, so from one invocation of f() to another, a may change.
 
F

Frederick Gotham

jpalecek posted:
Could you be more specific?


Are you familiar with CPU instructions?

Here's sample snippet (1):

int main()
{
int i = 7;

int j = 8;

j += i;
}

In compiling "j += i", the compiler might do the following:

Use one instruction to load the value of "i" into a register.
Use one instruction to add the register value to "j".

Here's sample snippet (2):

int main()
{
int const i = 7;

int j = 8;

j += i;
}

In compiling "j += i", the compiler might do the following:

Use one instruction to add 7 to "j".
 
I

Ian Collins

Frederick said:
jpalecek posted:





Are you familiar with CPU instructions?

Here's sample snippet (1):

int main()
{
int i = 7;

int j = 8;

j += i;
}

In compiling "j += i", the compiler might do the following:

Use one instruction to load the value of "i" into a register.
Use one instruction to add the register value to "j".
more like

Use one instruction to load the value of "j" into a register.
Use one instruction to load the value of "i" into a register.
Use one instruction to add the second register value to the first.
Use one instruction to set the value of "j" from a register.
Here's sample snippet (2):

int main()
{
int const i = 7;

int j = 8;

j += i;
}

In compiling "j += i", the compiler might do the following:

Use one instruction to add 7 to "j".
more like

Use one instruction to load the value of "j" into a register.
Use one instruction to add 7 to the register..
Use one instruction to set the value of "j" from a register.

Which may, or may not be faster than the first, depending on whether
immediate addition is a) supported, b) fast or slow.

Not a good example!
 
T

Thomas Matthews

Jiří PaleÄek said:
Hello,

I know the rules for const handling in C++, but I'd like to ask what is
the "right" way to use them, eg. when is it appropriate to make a member
function const?

My philosophy is to use "const" to help you catch errors during compile
time. If the method (member function) does not alter the state (member
variables) it should be labeled as const.
This came across this question when I was thinking about implementation
of a class implementing some lazy data structure or cache. The C++ rules
allow among other possibilities making all member functions non-const,
or making all member functions const and all members mutable. These are
the extreme possibilities, but in cases I was thinking about, the
differences are more subtle. Imagine, for example, a lazy data
structure, like the union-find data structure with path compression.
This data structure maintains a partition of some set and has two major
operations:

1. union: for two representatives of subsets in the partition, alter the
data structure so the partition contains all subsets except the two,
which are replaced by their union.

2. find: for a member of the set, find a representative of the subset it
is in.

From this description, it may seem that union is non-const and find is
const. But if you look at the implementation, the find alters the data
structure. So is it const or not?

Is const more like "doesn't touch the inner state of the object" or "you
won't notice the state changed"?

What is the compilers' view on this? Do they use const to make some
optimizations? Is there any danger to use cast to remove the const
(which clearly violates the purpose of the const)?

Yes, compiler's use the const-ness of a variable to determine the
appropriate optimization (or none). For example, identifiers declared
as const can be placed into read-only memory (which a computer may have
more of than read-write memory).
Regards
Jiri Palecek

Look up the keyword "mutable".

I have a proxy class for a variable in a database. I have declared
the method get_value() as const because returning a value should not
alter the state of the class. However, I have declared the member
as "mutable" so that it can be read from a table if necessary.
The "const" get_value() method agrees with the interface, and the
"mutable" allows for lazy acquisition of the value (e.g. on
demand acquisition).

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.comeaucomputing.com/learn/faq/
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library
 
F

Frederick Gotham

Frederick Gotham posted:
There are exceptions however:

(1) Functions which return by value
(2) Casts which yield an R-value


I just thought of another:

(3) Member functions which don't alter member objects, but which alter
dynamically acquired resources.
 
J

jpalecek

Frederick said:
jpalecek posted:



int main()
{
int const i = 7;

int j = 8;

j += i;
}

Yes, but a decenntly clever compiler will optimize the 7 even
without the const (it's easy to tell from the code that i is 7 at the
time of addition). But will compiler do this in the following
example?

extern int f(const int&);

int main()
{
int const i =7;
f(i);
int j=8+i;
}

Regards
Jiri Palecek
 
F

Frederick Gotham

jpalecek posted:
Yes, but a decenntly clever compiler will optimize the 7 even
without the const (it's easy to tell from the code that i is 7 at the
time of addition).


Yes, you're probably right. However, "const" gives us a few things:

(1) It expresses the intent to NOT change the data.
(2) It prohibits the compiler from changing the data willy-nilly.
(3) It encourages optimisation.

But will compiler do this in the following
example?

extern int f(const int&);

int main()
{
int const i =7;
f(i);
int j=8+i;
}


I would certainly hope so. It would be undefined behaviour for the function
"f" to alter the data -- the compiler shouldn't make allowances for
undefined behaviour. Here's a slightly more controversial one though:

int f(int const &); /* Defined elsewhere */

int main()
{
int i = 7;

f(i);

int j = 8 + i;
}

It's possible that "f" can alter "i" *without* invoking undefined
behaviour, but... given that it takes its argument by reference to const,
should the compiler be allowed to assume that it won't change it, and thus
optimise to the following:

int j = 15;
 
M

Marcus Kwok

Have a look at the "Const correctness" section of the FAQ. In
particular, 18.10 says,

The trailing const on [a] member function means that the abstract
(client-visible) state of the object isn't going to change. This is
slightly different from promising that the "raw bits" of the object's
struct aren't going to change.

So, I would say that it would be more for logical constness ("you won't
notice the state changed").

Yes, but does the next sentence

C++ compilers aren't allowed to take the "bitwise" interpretation
unless they can solve the aliasing problem, which normally can't be
solved (i.e., a non-const alias could exist which could modify the
state of the object)

imply (very fuzzily though) that compiler can use the "bitwise" rule if
they can
solve the aliasing issue (eg. by __restricted, or by simply not having
any
other pointer around)?

Someone posted this link in another thread. It may help illuminate some
uses of const:

http://www.gotw.ca/gotw/081.htm
 

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,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top