What's the difference between "Type&..­" with "const Type&.."

F

fl

Hi,
I am new to C++. The following are from the example of book C primer.
I don't know the difference of them even after I look through the
book. Especially the second "const" of the last line is curious to me.
Thank you in advance.




// return element from head of Queue
// unchecked operation: front on an empty Queue is undefined
Type& front() { return head->item; }
const Type &front() const { return head->item; }
 
J

Jim Langston

fl said:
Hi,
I am new to C++. The following are from the example of book C primer.
I don't know the difference of them even after I look through the
book. Especially the second "const" of the last line is curious to me.
Thank you in advance.




// return element from head of Queue
// unchecked operation: front on an empty Queue is undefined
Type& front() { return head->item; }

front returns a reference to a Type that is modifiable (not constant) and
the function can change the instance of the class it was called on.
const Type &front() const { return head->item; }

front returns a constant type reference (the instance of Type can not be
changed via the reference, it's constant) and the function can not change
the instance of the class it was called on. The function itself is
constant.
 
F

fl

front returns a reference to a Type that is modifiable (not constant) and
the function can change the instance of the class it was called on.


front returns a constant type reference (the instance of Type can not be
changed via the reference, it's constant) and the function can not change
the instance of the class it was called on.  The function itself is
constant.

Thank you very much. Both of the functions have the same function
body. There are four combinations with "const" and non-const in the
two definitions. The two definitions give two of the specific types to
exclude the other two:
const Type &front() { return head->item; }
Type &front() const { return head->item; }

Right?
 
J

Jim Langston

fl said:
Thank you very much. Both of the functions have the same function
body. There are four combinations with "const" and non-const in the
two definitions. The two definitions give two of the specific types to
exclude the other two:
const Type &front() { return head->item; }
Type &front() const { return head->item; }

Right?

Both of these function declarations should be front() const for constant
correctness. The function does not modify the class.

However, you can't do this, as you can't override a function by return value
only. It seems that it would make more sense for there to be only one
function declaration:

Type& front() const { return head->item; }

The general rule is, use const wherever you can for constant correctness.
If a function does not modify the instance, declare if const. If the
parameters are not modified by the function, declare them const. This
applies to pointers and references, it has a little different effect on
biult in types. I.E.

void Foo( const int i ) { /*...*/ }
changes to i can not be seen outside the function since they are passed by
value anyway. However, it means that you can't change i inside the function
itself which some people may do (I've never liked that though).

I.e.
void Count( int i ) { while ( i > 0 ) std::cout << i-- << "\n"; }

You can't do that if it is Count( const int i ).

I don't think most people bother to make built in types const, or rather
I've found no real meaningful reason too.

You should read the C++ FAQ on "const correctness". It explains it a lot
better than I can.
http://www.new-brunswick.net/workshop/c++/faq/const-correctness.html
 
J

Jim Langston

Jim said:
Both of these function declarations should be front() const for
constant correctness. The function does not modify the class.

However, you can't do this, as you can't override a function by
return value only. It seems that it would make more sense for there
to be only one function declaration:

Type& front() const { return head->item; }

The general rule is, use const wherever you can for constant
correctness. If a function does not modify the instance, declare if
const. If the parameters are not modified by the function, declare
them const. This applies to pointers and references, it has a little
different effect on biult in types. I.E.

void Foo( const int i ) { /*...*/ }
changes to i can not be seen outside the function since they are
passed by value anyway. However, it means that you can't change i
inside the function itself which some people may do (I've never liked
that though).
I.e.
void Count( int i ) { while ( i > 0 ) std::cout << i-- << "\n"; }

You can't do that if it is Count( const int i ).

I don't think most people bother to make built in types const, or
rather I've found no real meaningful reason too.

You should read the C++ FAQ on "const correctness". It explains it a
lot better than I can.
http://www.new-brunswick.net/workshop/c++/faq/const-correctness.html

Specially, 18.12 seems to deal with what you are looking at.
http://www.new-brunswick.net/workshop/c++/faq/const-correctness.html#faq-18.12
 
E

Erik Wikström

Both of these function declarations should be front() const for constant
correctness. The function does not modify the class.

However, you can't do this, as you can't override a function by return value
only. It seems that it would make more sense for there to be only one
function declaration:

Type& front() const { return head->item; }

Actually the reason for having two versions is that the user should be
able to modify the object returned (or referred to by the reference
returned) by front() if the object you called front on is non-const. But
if the object is const you can not modify the elements.

This is a common idiom for containers (which I suspect the example is
taken from) and it is the reason whey both begin() and end() have a
const and a non-const version on the standard library containers.
 
J

Jim Langston

Erik said:
Actually the reason for having two versions is that the user should be
able to modify the object returned (or referred to by the reference
returned) by front() if the object you called front on is non-const.
But if the object is const you can not modify the elements.

This is a common idiom for containers (which I suspect the example is
taken from) and it is the reason whey both begin() and end() have a
const and a non-const version on the standard library containers.

Yes, after I posted that I read through the FAQ again to refresh my memory
when I came across 18.12 which states this exact sample and posted the
follow up.
 
T

Tobias

Hello,
when I red the thread I've got the impression that your question
The two definitions give two of the specific types to
exclude the other two:
const Type &front() { return head->item; }
Type &front() const { return head->item; }

was not clearly answered. You are right. Both constructs should NOT be
used (assuming that head is a non-mutable member of (*this)).

The first one is bad style. The function front() should be const since
it does not change the member and the member can also not be changed
from the caller.

The second one should even be rejected by the compiler since returning
the non-const member-reference gives the caller the possiblility to
change it (and therefore the object). Thus, the second front function
is NOT const.

Hope, this is clearly stated (section 18.11 of the FAQ).

Best regards,
Tobias
 
J

James Kanze

when I red the thread I've got the impression that your question
was not clearly answered. You are right. Both constructs
should NOT be used (assuming that head is a non-mutable member
of (*this)).
The first one is bad style. The function front() should be
const since it does not change the member and the member can
also not be changed from the caller.
The second one should even be rejected by the compiler since
returning the non-const member-reference gives the caller the
possiblility to change it (and therefore the object). Thus,
the second front function is NOT const.

The compiler only enforces shallow const. The second one
returns a reference to something pointed to, so it is fine as
far as the compiler is concerned. It is still generally bad
practice, since it allows the client code to modify the logical
value of the object through a const reference.

The usual alternatives are, of course:
Type const& front() const { return head->item ; }
Type& front() { return head->item ; }
The first allows no modification, even by the client which calls
it, and the second allows modification by the client of the
logical value of the object.

And of course, which one is called depends on the const-ness of
the object, and not on what is done with or through the return
value.
 
T

Tobias

/*
Hello,
thanks for the replay.
You said:
The compiler only enforces shallow const.

If so -- does

"gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)"

get this wrong?

Best regards,
Tobias
*/

class T
{
int x;
int& getref() const {
return x;
}
};

/*
Command line:

g++ -c test.cc

Output:

test.cc: In member function 'int& T::getref() const':
test.cc:6: error: invalid initialization of reference of type 'int&'
from expression of type 'const int'

*/
 
T

Tobias

Okay said:
class T
{
int x;
int& getref() const {
return x;
}

};

IS really shallow and therefore the compiler rightly cries.
It's the pointer-thing. The following will work
(as it works also for the example of the OP):

class T
{
int *x;
int& getref() const {
return *x;
}

};

My fault. Sorry for the noise.

Best regards,
Tobias
 
E

Erik Wikström

/*
Hello,
thanks for the replay.


If so -- does

"gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)"

get this wrong?

Best regards,
Tobias
*/

class T
{
int x;
int& getref() const {
return x;
}
};

If the function is const then the return-value should probably also be
const (or return by value):

const int& getref() const {
return x;
}

And if you want to be able to modify x through getref() as long as the
object itself is not const you can add:

int& getref() {
return x;
}

And test like this:

int main()
{
T t;
const T& ct = t;
t.getref() = 5;
ct.getref() = 4; // will not work since ct is const
}
 
J

James Kanze

If so -- does
"gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)"
get this wrong?

Not in any case I know of.
class T
{
int x;
int& getref() const {
return x;
}
};
/*
Command line:
g++ -c test.cc

test.cc: In member function 'int& T::getref() const':
test.cc:6: error: invalid initialization of reference of type 'int&'
from expression of type 'const int'

Which is correct. You've violated shallow const, trying to get
a non-const reference to an immediate member in const member
function.
 

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,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top