Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Guru of the Week #6: Solution

2,390 views
Skip to first unread message

Herb Sutter

unread,
Mar 29, 1997, 3:00:00 AM3/29/97
to

.--------------------------------------------------------------------.
| Guru of the Week problems and solutions are posted weekly on |
| news:comp.lang.c++.moderated. For past problems and solutions, |
| see the GotW archive at http://www.cntc.com. |
| Is there a topic you'd like to see covered? mailto:he...@cntc.com |
`--------------------------------------------------------------------'

GotW #6: Const-Correctness

Difficulty: 6 / 10

[Warning: This solution contains points that some may find arguable.]

Congratulations to Christian Millour and Mark Roulo, the Gurus of the
Week! Apparently many solvers thought that this problem was on the easy
side, and addressed the more-usual const issues. Only Christian
pointed out the correction to const_iterator and only Mark pointed out
one of the possible const return values. Well done!

> class Polygon {
> public:
> Polygon() : area_(-1) {}
>
> void AddPoint( const Point pt ) { InvalidateArea();
> points_.push_back(pt); }

1. Since the point object is passed by value, there is little
or no benefit to declaring it const.

> Point GetPoint( const int i ) { return points_[i]; }

2. Same comment about the parameter as above. Normally const
pass-by-value is unuseful and misleading at best.

3. This should be a const member function, since it doesn't
change the state of the object.

4. (Arguable.) Return-by-value should normally be const for
non-builtin return types. This assists client code by
making sure the compiler emits an error if the caller
tries to modify the temporary (for example,
"poly.GetPoint(i) = Point(2,2);"... after all, if this was
intended to work, GetPoint() should have used
return-by-reference and not return-by-value in the first
place, and as we will see later it makes sense for GetPoint()
to return a const value or a const reference since it should
be usable on const Polygon objects in operator+()).

Note: Lakos (pg. 618) argues against returning const
value, and notes that it is redundant for builtins
anyway (for example, returning "const int"), which
he notes may interfere with template instantiation.

[Guideline] When using return-by-value for non-builtin
return types, prefer returning a const value.

> int GetNumPoints() { return points_.size(); }

5. Again, the function should be const.

(It should not return 'const int' in this case, though,
since the int is already an rvalue and to put in 'const'
can interfere with template instantiation and is confusing,
misleading, and probably fattening.)

> double GetArea() {
> if( area_ < 0 ) // if not yet calculated and cached
> CalcArea(); // calculate now
> return area_;
> }

6. Even though it modifies the object's internal state, this
function should be const because the object's observable
state is unchanged (we are doing some caching, but that's
an implementation detail and the object is logically const).
This means that area_ should be declared mutable. If your
compiler doesn't support mutable yet, kludge this with a
const_cast of area_ (and a comment to remove the cast when
mutable is available!), but do make the function const.

> private:
> void InvalidateArea() { area_ = -1; }

7. While this one is debatable, I'd still recommend that this
function ought also to be const, even if for no other
reason than consistency. (Granted, semantically it will only
be called from non-const functions, since its purpose is to
invalidate the cached area_ when the object's state changes.)

> void CalcArea() {
> area_ = 0;
> vector<Point>::iterator i;
> for( i = points_.begin(); i != points_.end(); ++i )
> area_ += /* some work */;
> }

8. This member function definitely should be const. After all,
it will be called from another const member function, namely
GetArea().

9. Since the iterator should not change the state of the points_
collection, it ought to be a const_iterator.

> vector<Point> points_;
> double area_;
> };
>
> Polygon operator+( Polygon& lhs, Polygon& rhs ) {

10. Pass by const references, of course.

11. Again, return-by-value should be const.

> Polygon ret = lhs;
> int last = rhs.GetNumPoints();

12. Since 'last' should never change, say so with "const int".

> for( int i = 0; i <= last; ++i ) // concatenate
> ret.AddPoint( rhs.GetPoint(i) );

(Another reason why GetPoint() should be a const member
function, returning either const value or const reference.)

> return ret;
> }
>
> void f( const Polygon& poly ) {
> const_cast<Polygon&>(poly).AddPoint( Point(0,0) );

Bonus: This result is undefined if the referenced object is
declared as const (which it is in the case of f(cpoly)
below). The parameter isn't really const, so don't
declare it as const!

> }
>
> void g( Polygon& const rPoly ) { rPoly.AddPoint( Point(1,1) ); }

13. This 'const' is useless, since references cannot be changed
to refer to a different object anyway.

> void h( Polygon* const pPoly ) { pPoly->AddPoint( Point(2,2) ); }

14. This 'const' is equally useless, but for a different reason:
since you're passing the pointer by value, this makes as
little sense as passing a parameter 'const int' above.

(If your answer to the bonus part said something about
these functions being uncompilable... sorry, they're
quite legal C++. You were probably thinking of putting
the 'const' to the left of the & or *, which would have
made the function body illegal.)

> int main() {
> Polygon poly;
> const Polygon cpoly;
> f(poly);

This is fine.

> f(cpoly);

This causes undefined results when f() tries to cast away the
constness of and then modify its parameter.

> g(poly);

This is fine.

> h(&poly);

This is fine.

> }


That's it. Here's a corrected version (remember, correcting
const only, not other poor style):

class Polygon {
public:
Polygon() : area_(-1) {}

void AddPoint( Point pt ) { InvalidateArea();
points_.push_back(pt); }
const Point GetPoint( int i ) const { return points_[i]; }
int GetNumPoints() const { return points_.size(); }

double GetArea() const {
if( area_ < 0 ) // if not yet calculated and cached
CalcArea(); // calculate now
return area_;
}

private:
void InvalidateArea() const { area_ = -1; }

void CalcArea() const {
area_ = 0;
vector<Point>::const_iterator i;
for( i = points_.begin(); i != points_.end(); ++i )
area_ += /* some work */;
}

vector<Point> points_;
mutable double area_;
};

const Polygon operator+( const Polygon& lhs,
const Polygon& rhs ) {
Polygon ret = lhs;
const int last = rhs.GetNumPoints();
for( int i = 0; i <= last; ++i ) // concatenate
ret.AddPoint( rhs.GetPoint(i) );
return ret;
}

void f( Polygon& poly ) {
poly.AddPoint( Point(0,0) );
}

void g( Polygon& rPoly ) { rPoly.AddPoint( Point(1,1) ); }

void h( Polygon* pPoly ) { pPoly->AddPoint( Point(2,2) ); }

int main() {
Polygon poly;
f(poly);
g(poly);
h(&poly);
}


---
Herb Sutter (mailto:he...@cntc.com)

Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


Oleg Zabluda

unread,
Mar 31, 1997, 3:00:00 AM3/31/97
to

Herb Sutter <he...@cntc.com> wrote:
: > void AddPoint( const Point pt ) { InvalidateArea();
: > points_.push_back(pt); }

: 1. Since the point object is passed by value, there is little
: or no benefit to declaring it const.

: > Point GetPoint( const int i ) { return points_[i]; }

: 2. Same comment about the parameter as above. Normally const
: pass-by-value is unuseful and misleading at best.

I don't understand both those comments. First of all, I don't see what
harm does it make to declare formal arguments as const. I don't see
how it can be misleading. The difference between formal and actual
arguments is almost the very the first thing a programmer learns.

The advantages of declaring them const, as I see, are:

1. Sanity. Whatever never changes, should be const.

2. Self documentation. This tells the maintainer that the function is
implemented in a way that does not change it's actual argument.

3. Eliminates the need to think if push_back() expects a reference or
a const reference or is overloaded for both or whatever, especially
if points_ will be redeclared later to be user-defined non-STL
container.

4. Provides an actual argument of the correct type to (overloaded)
functions, possibly called by push_back().

5. Gives a compiler a hint on possible optimizations. It doesn't have to
analyze tons of code to see that i never changes it's value. It still
must check it, but it's much easier.

6. Provides a compiler with a more informative declaration of the function,
even if it doesn't see the definition. For example, this way it knows
that if it put i onto a register for GetPoint() to read, it's value is
not going to be changed after GetPoint() returns. So it can reuse the
value in the register without restoring it first.

Do I make any sense?

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.

Christian Millour

unread,
Apr 1, 1997, 3:00:00 AM4/1/97
to

Herb Sutter wrote:
> >
> > void AddPoint( const Point pt ) { InvalidateArea();
> > points_.push_back(pt); }
>
> 1. Since the point object is passed by value, there is little
> or no benefit to declaring it const.
>
> > Point GetPoint( const int i ) { return points_[i]; }
>
> 2. Same comment about the parameter as above. Normally const
> pass-by-value is unuseful and misleading at best.
>

I am concerned about the "little or no" and "Normally" bits,
although I understand that definitive statements are had
to make. Anyway, are there indeed cases where it makes sense
to pass a parameter by const value ?

> [snip]

>
> > void CalcArea() {
> > area_ = 0;
> > vector<Point>::iterator i;
> > for( i = points_.begin(); i != points_.end(); ++i )
> > area_ += /* some work */;
> > }
>
> 8. This member function definitely should be const. After all,
> it will be called from another const member function, namely
> GetArea().
>
> 9. Since the iterator should not change the state of the points_
> collection, it ought to be a const_iterator.
>

English is not my mother language so please pardon any
misunderstanding. IMHO as soon as you make CalcArea a const member,
i *has* to be a const_iterator (won't compile otherwise). Anyway my
point is that your last sentence above can be understood as `when
traversing a collection in a const manner (inspection only), use a
const iterator, irrespective of whether the collection is const or
non-const' (sorry about the formulation). This may lead to
uncompilable code, as there is no provision in CD2 for conversion
from reverse_iterators to const_reverse_iterators. e.g.
list<whatever> l;
for (list<whatever>::const_reverse_iterator i = l.begin();
i != l.end();
++i) {}
currently doesn't compile (can't assign, can't compare).


> That's it. Here's a corrected version (remember, correcting
> const only, not other poor style):
>

> const Polygon operator+( const Polygon& lhs,
> const Polygon& rhs ) {
> Polygon ret = lhs;
> const int last = rhs.GetNumPoints();
> for( int i = 0; i <= last; ++i ) // concatenate
> ret.AddPoint( rhs.GetPoint(i) );
> return ret;
> }

Sorry, I can't resist. Zero_based also ;-) means that 'last'
is 'past the end', so the loop test should read i < last. As
previously, not a const issue tho...


Please rest assured that I appreciate and respect immensely your
pedagogical endeavor. Keep up the good work!

Regards.

Herb Sutter

unread,
Apr 1, 1997, 3:00:00 AM4/1/97
to

Oleg Zabluda <zab...@math.psu.edu> wrote:
>Herb Sutter <he...@cntc.com> wrote:
>: > void AddPoint( const Point pt ) { InvalidateArea();

>: > points_.push_back(pt); }
>
>: 1. Since the point object is passed by value, there is little
>: or no benefit to declaring it const.
>
>: > Point GetPoint( const int i ) { return points_[i]; }
>
>: 2. Same comment about the parameter as above. Normally const
>: pass-by-value is unuseful and misleading at best.
>
>I don't understand both those comments.

Actually, you're demonstrating why it's misleading, since I think you're
thinking of pass-by-reference. :-) Here, both pt and i are copied anyway...
whether they're declared const or not doesn't affect the caller at all.

>The advantages of declaring them const, as I see, are:
>
>1. Sanity. Whatever never changes, should be const.
>
>2. Self documentation. This tells the maintainer that the function is
> implemented in a way that does not change it's actual argument.

[more similar comments]

It never will, since the function always uses only a copy of the actual
argument. That's why there's no benefit to declaring the parameter as const.

Steve Willer

unread,
Apr 1, 1997, 3:00:00 AM4/1/97
to

Oleg Zabluda <zab...@math.psu.edu> wrote:

>: 2. Same comment about the parameter as above. Normally const


>: pass-by-value is unuseful and misleading at best.
>

>I don't understand both those comments. First of all, I don't see what
>harm does it make to declare formal arguments as const. I don't see
>how it can be misleading. The difference between formal and actual
>arguments is almost the very the first thing a programmer learns.
>

>The advantages of declaring them const, as I see, are:
>
>1. Sanity. Whatever never changes, should be const.

Often you will see functions changing the values of their parameters on
purpose. The parameters, when passed by value, serve as convenient
temporaries, and it may allow the programmer to avoid the creation of
more temporaries.

>2. Self documentation. This tells the maintainer that the function is
> implemented in a way that does not change it's actual argument.

That's self-evident by the fact that it's passed by value. The const
doesn't make any difference to this.

>6. Provides a compiler with a more informative declaration of the function,
> even if it doesn't see the definition. For example, this way it knows
> that if it put i onto a register for GetPoint() to read, it's value is
> not going to be changed after GetPoint() returns. So it can reuse the
> value in the register without restoring it first.

Sounds interesting. Do any compilers actually do this, though? After
all, the function can always const_cast the parameter and end up
changing the caller's object.

Alexandre Oliva

unread,
Apr 1, 1997, 3:00:00 AM4/1/97
to

Oleg Zabluda writes:

> Herb Sutter <he...@cntc.com> wrote:
> : > void AddPoint( const Point pt ) { InvalidateArea();
> : > points_.push_back(pt); }

> : 1. Since the point object is passed by value, there is little
> : or no benefit to declaring it const.

> : > Point GetPoint( const int i ) { return points_[i]; }

> : 2. Same comment about the parameter as above. Normally const
> : pass-by-value is unuseful and misleading at best.

> I don't understand both those comments. First of all, I don't see what


> harm does it make to declare formal arguments as const. I don't see
> how it can be misleading. The difference between formal and actual
> arguments is almost the very the first thing a programmer learns.

A formal argument is never const, as it seems to be in the former
examples. However, the actual argument the callee gets will be, which
does not mean the caller should consider the argument it passes on is
unchanged.


> The advantages of declaring them const, as I see, are:

> 1. Sanity. Whatever never changes, should be const.

Agreed.

> 2. Self documentation. This tells the maintainer that the function is
> implemented in a way that does not change it's actual argument.

^ (no apostrophe
here:-)

Partially agreed. Although this is good for the actual definition of
the method, it is not good for its declaration. The fact that the
actual argument will not be changed (from the point of view of the
caller) depends on whether the argument is passed by value or by
reference.

> 3. Eliminates the need to think if push_back() expects a reference or
> a const reference or is overloaded for both or whatever, especially
> if points_ will be redeclared later to be user-defined non-STL
> container.

Adding `const' to the actual type of the formal argument is not the
same as const-qualifying types that are part of the type-specification
fo the formal argument. Let me give an example for clarity:

void foo1(const int& arg1); // or (int const& arg1)
void foo2(int& arg2);
void foo3(const int arg3);
void foo4(int arg4);

The types of foo1 and foo2 are different, as are the types of arg1 and
arg2, and it does make a difference for overload resolution,
reference-binding, etc. Notice that, in these examples, the
const-qualifier qualifies a type (int) that is not the actual type of
the formal argument (that is reference to [const] int).

However, the types of foo3 and foo4 are exactly the same, both are
void(int), functions that return void and get an int argument by
value. The const specification just refers to the actual type of
arg3/arg4 inside the function body.


Besides, if you care about a possible non-STL container, you'd better
not get the argument as const, since the non-STL container might
expect a reference to non-const.

> 4. Provides an actual argument of the correct type to (overloaded)
> functions, possibly called by push_back().

I don't understand the gain here.

> 5. Gives a compiler a hint on possible optimizations. It doesn't have to
> analyze tons of code to see that i never changes it's value. It still
> must check it, but it's much easier.

Agreed.

> 6. Provides a compiler with a more informative declaration of the function,
> even if it doesn't see the definition. For example, this way it knows
> that if it put i onto a register for GetPoint() to read, it's value is
> not going to be changed after GetPoint() returns. So it can reuse the
> value in the register without restoring it first.

In the declaration, the const will be simply ignored, and in fact it
can't be used for anything. The fact that it *can't* change won't
gain much for the caller, since, although the actual variable won't
change, the register allocated to store its value may be reallocated
for another variable, so you lose.

--
Alexandre Oliva
mailto:ol...@dcc.unicamp.br mailto:aol...@acm.org
Universidade Estadual de Campinas, SP, Brasil

Valentin Bonnard

unread,
Apr 1, 1997, 3:00:00 AM4/1/97
to

Steve Willer wrote:

>
> Oleg Zabluda <zab...@math.psu.edu> wrote:
> >6. Provides a compiler with a more informative declaration of the function,
> > even if it doesn't see the definition. For example, this way it knows
> > that if it put i onto a register for GetPoint() to read, it's value is
> > not going to be changed after GetPoint() returns. So it can reuse the
> > value in the register without restoring it first.
>
> Sounds interesting. Do any compilers actually do this, though? After
> all, the function can always const_cast the parameter and end up
> changing the caller's object.

Not if the first var is const:

void foo (const int& i)
{
bar1 (i); // valid : increment i (ugly style)
while (i<1000) // can't put i into reg: i might be aliased
.... // and changed here
}

void foo (const int i)
{
bar2 (i); // can't call bar1 here
while (i<1000) // CAN put i into reg: i might be aliased
.... // but can't changed
}

void bar1 (const int& i) { ++ const_cast<int&> (i); }
void bar2 (const int& i) { cout << i; }

--

Valentin Bonnard
mailto:bonn...@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)

Vadim Zeitlin

unread,
Apr 2, 1997, 3:00:00 AM4/2/97
to

On 1 Apr 1997 05:12:59 -0500, he...@cntc.com (Herb Sutter) wrote:

>Oleg Zabluda <zab...@math.psu.edu> wrote:
>>Herb Sutter <he...@cntc.com> wrote:
>>: > void AddPoint( const Point pt ) { InvalidateArea();


>>: > points_.push_back(pt); }
>>
>>: 1. Since the point object is passed by value, there is little
>>: or no benefit to declaring it const.
>>

...


>>I don't understand both those comments.
>

>>The advantages of declaring them const, as I see, are:
>>
>>1. Sanity. Whatever never changes, should be const.
>>

>>2. Self documentation. This tells the maintainer that the function is
>> implemented in a way that does not change it's actual argument.

>[more similar comments]
>
>It never will, since the function always uses only a copy of the actual
>argument. That's why there's no benefit to declaring the parameter as const.

I believe that Oleg understands it. What (I think) he says is that in
this case the declaration above ensures that the function won't change
the copy of the actual parameter. Granted, it will never affect the
calling function (weather it changes it or not), but perhaps the
compiler would be able to optimize the code of our function somehow.

This being said, I still don't think that this is a sufficient reason
to prefer "void Foo(const int Bar)" compared to "void Foo(int Bar)":

1) You're very right when you say that it may be misleading, you might
think that the actual signature is "void Foo(const int *Bar)"...

2) Callers of this function don't really care if it changes or not
it's local copy of parameter, so they need not know about it.

3) Any modern optimizing compiler should be able to understand itself
that the local parameter is const.

4) It's just too wierd for a normal C/C++ programmer :-)

VZ

Marcelo Cantos

unread,
Apr 2, 1997, 3:00:00 AM4/2/97
to

Oleg Zabluda <zab...@math.psu.edu> writes:

>
> Herb Sutter <he...@cntc.com> wrote:
> : > void AddPoint( const Point pt ) { InvalidateArea();


> : > points_.push_back(pt); }
>
> : 1. Since the point object is passed by value, there is little
> : or no benefit to declaring it const.
>
> : > Point GetPoint( const int i ) { return points_[i]; }
>
> : 2. Same comment about the parameter as above. Normally const
> : pass-by-value is unuseful and misleading at best.
>

> I don't understand both those comments. First of all, I don't see what
> harm does it make to declare formal arguments as const. I don't see
> how it can be misleading. The difference between formal and actual
> arguments is almost the very the first thing a programmer learns.
>

> The advantages of declaring them const, as I see, are:
>
> 1. Sanity. Whatever never changes, should be const.

I would prefer to say that whatever *mustn't* change should be const.
If there is a particular variable that may or may not change without
affecting client code, then why bother?

This is the case with passing parameters by copy; the client couldn't
care less whether the copy is going to be modified or not. More to
the point, if it turns out later that the code can be made more
efficient by modifying the parameter, the interface has to be changed.

I also believe that a copy is mandatory since const can be cast-away
and the parameter modified anyway. I don't know what the standard
says about this, but it would have to say something like, "casting
away const on a parameter is undefined behaviour". Very dodgy.


--
______________________________________________________________________
Marcelo Cantos, Research Assistant __/_ mar...@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT / _ Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053 Aus/ralia ><_> Fax 61-3-9282-2490
Acknowledgements: errors - me; wisdom - God; funding - RMIT

James Kanze

unread,
Apr 2, 1997, 3:00:00 AM4/2/97
to

wil...@interlog.com (Steve Willer) writes:

|> Oleg Zabluda <zab...@math.psu.edu> wrote:
|>
|> >: 2. Same comment about the parameter as above. Normally const


|> >: pass-by-value is unuseful and misleading at best.
|> >

|> >I don't understand both those comments. First of all, I don't see what
|> >harm does it make to declare formal arguments as const. I don't see
|> >how it can be misleading. The difference between formal and actual
|> >arguments is almost the very the first thing a programmer learns.
|> >
|> >The advantages of declaring them const, as I see, are:
|> >
|> >1. Sanity. Whatever never changes, should be const.
|>

|> Often you will see functions changing the values of their parameters on
|> purpose. The parameters, when passed by value, serve as convenient
|> temporaries, and it may allow the programmer to avoid the creation of
|> more temporaries.

I think the point is that the function declaration defines the contract
with the user. The "const" is confusing, as it suggests that in its
absence, the function might modify the values. Which is, of course,
impossible, because the function only gets a copy anyway, and not the
originals.

The compiler ignores the const completely (or should, according to the
draft standard).

|> >2. Self documentation. This tells the maintainer that the function is
|> > implemented in a way that does not change it's actual argument.
|>

|> That's self-evident by the fact that it's passed by value. The const
|> doesn't make any difference to this.
|>

|> >6. Provides a compiler with a more informative declaration of the function,
|> > even if it doesn't see the definition. For example, this way it knows
|> > that if it put i onto a register for GetPoint() to read, it's value is
|> > not going to be changed after GetPoint() returns. So it can reuse the
|> > value in the register without restoring it first.
|>
|> Sounds interesting. Do any compilers actually do this, though? After
|> all, the function can always const_cast the parameter and end up
|> changing the caller's object.

There are two different aspects here. One is the function declaration,
which is seen by the user, and the other is the implementation. In a
function declaration, the const is simply ignored. Period.

In a function definition, the const is meaningful within the body of the
function. In practice, it probably doesn't buy you much, because
function arguments are local variables, and the compiler can generally
see what is going on anyway, but I wouldn't object to using the const in
a definition.

Note that there is no requirement that the const-ness be the same in the
declaration and the definition. The functions "void f( int )" and "void
f( int const )" are one and the same. Within the body of the function,
it is the const in the definition which counts; the const-ness of the
declarations are irrelevant.

Since whether the function modifies its private copy or not has no
effect on the interface, I would recommend the rule of never declaring
an argument const in a declaration.

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

Christian Millour

unread,
Apr 2, 1997, 3:00:00 AM4/2/97
to

I goofed:
> ...there is no provision in CD2 for conversion

> from reverse_iterators to const_reverse_iterators. e.g.
> list<whatever> l;
> for (list<whatever>::const_reverse_iterator i = l.begin();
> i != l.end();
> ++i) {}
> currently doesn't compile (can't assign, can't compare).
please substitute rbegin (resp. rend) for begin (resp. end) above.

David A. Cuthbert

unread,
Apr 4, 1997, 3:00:00 AM4/4/97
to

James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
> wil...@interlog.com (Steve Willer) writes:

>> Oleg Zabluda <zab...@math.psu.edu> wrote


>>>1. Sanity. Whatever never changes, should be const.

>>Often you will see functions changing the values of their parameters on
>>purpose. The parameters, when passed by value, serve as convenient
>>temporaries, and it may allow the programmer to avoid the creation of
>>more temporaries.

>I think the point is that the function declaration defines the contract
>with the user. The "const" is confusing, as it suggests that in its
>absence, the function might modify the values. Which is, of course,
>impossible, because the function only gets a copy anyway, and not the
>originals.
>
>The compiler ignores the const completely (or should, according to the
>draft standard).

Huh? This doesn't make sense (regardless of whether its in the draft
standard).

const is more than a contract with the user -- it adds type safety,
preventing the function from altering the object. Borland 5.2
complains (IMHO rightfully) about the following:

int testfunc(int const h) {
++h;
return h;
}

with the error message "Cannot modify a const object."

Now, having said that, there uses of such code are few and far
between. I might use it if I suspected a library of altering
something it shouldn't be (and that something is a parameter passed by
value to my function). Adding const ensures that my code isn't the
one affecting the parameter (aside from the shady area of const_cast
and his thug friends :-).

{The point is that, in the above function parameter, there is
no reason for "const" to appear in the function declaration,
where it is only confusing/misleading for client coders. -hps}

--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University

Valentin Bonnard

unread,
Apr 6, 1997, 4:00:00 AM4/6/97
to

[I followup Herb Sutter and ask a tecknical question at the
same time;
moderator: if you can't handle the cross-post, just separate
the posts;
posters: choose the right group to followup]

{ I don't think it's fair to ask the moderators to `just separate
the posts'; depending on the moderator's setup, this may not be
a trivial task. -vdv }

Herb Sutter wrote:
> [Warning: This solution contains points that some may find arguable.]

Yes

> 1. Since the point object is passed by value, there is little
> or no benefit to declaring it const.
>
> > Point GetPoint( const int i ) { return points_[i]; }
>
> 2. Same comment about the parameter as above. Normally const
> pass-by-value is unuseful and misleading at best.

Has argued, it's arguable.

If the function contain a loop which is runned 100000 times
or if it's called 100000 times it can interresting to
make i const has it help the optimiser.

> 4. (Arguable.) Return-by-value should normally be const for
> non-builtin return types. This assists client code by
> making sure the compiler emits an error if the caller
> tries to modify the temporary (for example,
> "poly.GetPoint(i) = Point(2,2);"...

which isn't possible anyway so the argument doesn't hold
the way it's written.

[The std question:]
Why is operator= a special case in this respect ?

T () = T ();

isn't valid (as far as I know), but

T () += T ()

is. Is there a rationnal for the introduction of such
a special case ?

> after all, if this was
> intended to work, GetPoint() should have used
> return-by-reference and not return-by-value in the first

The user of the class can want to use the temporary directly.
I don't like the idea taht it's const (I don't like the way
C++ define that anyway).

> place, and as we will see later it makes sense for GetPoint()
> to return a const value or a const reference since it should
> be usable on const Polygon objects in operator+()).
>
> Note: Lakos (pg. 618) argues against returning const
> value, and notes that it is redundant for builtins
> anyway (for example, returning "const int"), which
> he notes may interfere with template instantiation.
>
> [Guideline] When using return-by-value for non-builtin
> return types, prefer returning a const value.

--

Valentin Bonnard
mailto:bonn...@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]


[ about comp.lang.c++.moderated. First time posters: do this! ]

---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]

Valentin Bonnard

unread,
Apr 6, 1997, 4:00:00 AM4/6/97
to

Vadim Zeitlin wrote:
> This being said, I still don't think that this is a sufficient reason
> to prefer "void Foo(const int Bar)" compared to "void Foo(int Bar)":

There was in one of the first posts a confusion betwen declaration
and definition; now the confusion is in the whole thread, expect
James' post (of course).

> 1) You're very right when you say that it may be misleading, you might
> think that the actual signature is "void Foo(const int *Bar)"...

In a declaration, any beginner will thing that const serve some
purpose; actually, it's ignored by the compiler:

void Foo (const int k );
^^^^^ ^^^^^ ^^^^^
ignored saved in ignored
some table (1)

This declaration don't mean that in the definition the
parameter will be named k or that it will be const.

// guess how it works...
void make_complex (float x, const float y);

void make_complex (const float rho, float theta) // wrong !
{

}

> 2) Callers of this function don't really care if it changes or not
> it's local copy of parameter, so they need not know about it.

Not only they need not to know, buyt they don't knwo even if
they see Foo (const int) vs Foo (int).

> 3) Any modern optimizing compiler should be able to understand itself
> that the local parameter is const.

It's very difficult and slow (read: impossible for most compiler
architecture) to do that if the variable is aliased (address taken/
reference built); in practice you can loose some copy propagation
by not declaring parameters const.

(And you can tell me that there is one compiler in world which
isn't afraid of that and that won't change the validity of
my assertion.)

> 4) It's just too wierd for a normal C/C++ programmer :-)

This thread prove that.

--

Valentin Bonnard
mailto:bonn...@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Marcelo Cantos

unread,
Apr 6, 1997, 4:00:00 AM4/6/97
to

da...@henry.ece.cmu.edu (David A. Cuthbert) writes:

> >I think the point is that the function declaration defines the contract
> >with the user. The "const" is confusing, as it suggests that in its
> >absence, the function might modify the values. Which is, of course,
> >impossible, because the function only gets a copy anyway, and not the
> >originals.
>

> Huh? This doesn't make sense (regardless of whether its in the draft
> standard).
>
> const is more than a contract with the user -- it adds type safety,
> preventing the function from altering the object. Borland 5.2
> complains (IMHO rightfully) about the following:

And so it should.

> int testfunc(int const h) {
> ++h;
> return h;
> }

But this actually obviates James's point. What is so bad about this:

int testfunc(int h) {
++h;
return h;
}

? There would only be a danger here if h was actually a reference to
the original variable, but it isn't, it's a copy; the callee can do
anything it darn well pleases with the copy and the caller couldn't
care less.

If you insist on making the parameter const, many functions which
would otherwise reuse the parameter as a local variable, eg:

void testfunc2(int h) {
while (--h) do_something();
}

Would have to be rewritten:

void testfunc2(int h) {
int j = h;
while (--j) do_something();
}

or:

void testfunc2(int h) {
for (int j = 0; j < h; ++j) do_something;
}

Both of which require an additional temporary for no conceivable gain.

Ironically, many compilers would take the above code and *still* reuse
the variable since it is clearly not required for any other purpose.

--
______________________________________________________________________
Marcelo Cantos, Research Assistant __/_ mar...@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT / _ Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053 Aus/ralia ><_> Fax 61-3-9282-2490
Acknowledgements: errors - me; wisdom - God; funding - RMIT

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John E. Potter

unread,
Apr 6, 1997, 4:00:00 AM4/6/97
to

David A. Cuthbert (da...@henry.ece.cmu.edu) wrote:

: James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
: > wil...@interlog.com (Steve Willer) writes:

: >> Oleg Zabluda <zab...@math.psu.edu> wrote
: >>>1. Sanity. Whatever never changes, should be const.

: >>Often you will see functions changing the values of their parameters on
: >>purpose. The parameters, when passed by value, serve as convenient
: >>temporaries, and it may allow the programmer to avoid the creation of
: >>more temporaries.

: >I think the point is that the function declaration defines the contract


: >with the user. The "const" is confusing, as it suggests that in its
: >absence, the function might modify the values. Which is, of course,
: >impossible, because the function only gets a copy anyway, and not the
: >originals.

: >The compiler ignores the const completely (or should, according to the
: >draft standard).

: Huh? This doesn't make sense (regardless of whether its in the draft
: standard).

: const is more than a contract with the user -- it adds type safety,
: preventing the function from altering the object. Borland 5.2
: complains (IMHO rightfully) about the following:

: int testfunc(int const h) {
: ++h;
: return h;
: }

: with the error message "Cannot modify a const object."

The point James is making is interesting; although, it seems not clear.

What he has suggested is that the declaration which is the contract
with the user should not contain const for a value parameter since it
is ignored by the compiler and can only confuse the client.

int testfunc (int); // header

But, it can make a difference in the implementation as you have
demonstrated above. It may make good sence to use const with a value
parameter in the implementation. James suggested that you go ahead
and use const in the implementation if it seems reasonable, but
never in the declaration.

My header above declares your function above. They are one in the
same according to the CD.

I like the idea. However, do not rush out to adopt the convention
since four of my five compilers give syntax errors if the declaration
and definition are in the same file and fail to link if in different
translation units. g++ gets it right. Give the others a few years
to catch up. Since USL cfront gets it wrong, I assume that this
const reading is a change from the ARM rules.

John

David A. Cuthbert

unread,
Apr 7, 1997, 3:00:00 AM4/7/97
to

Marcelo Cantos <mar...@mds.rmit.edu.au> wrote:
[re: function below as "int testfunc(int const h)"]

>But this actually obviates James's point. What is so bad about this:
>
> int testfunc(int h) {
> ++h;
> return h;
> }
>
>? There would only be a danger here if h was actually a reference to
>the original variable, but it isn't, it's a copy; the callee can do
>anything it darn well pleases with the copy and the caller couldn't
>care less.

There's nothing bad about it; however, I have problems with the notion
of the compiler just throwing the const away. (But, as John Potter
points out, "int testfunc(int const)" and "int testfunc(int)" should
have the same signature. (q.v.)) Whether or not it makes logical
design sense, the compiler should honor the restrictions that const
imposes -- even if they're silly.

>If you insist on making the parameter const, many functions which

>would otherwise reuse the parameter as a local variable [would have
>to be rewritten to use a temporary].

Yep. I'll agree that it's bad programming practice; I doubt you'll
find it in any of my programs (modulo typos where I omitted the & in
MyClass const&).

>Ironically, many compilers would take the above code and *still* reuse
>the variable since it is clearly not required for any other purpose.

Well, I'd expect that, actually. I'd be very upset, though, if it did
that to an "int const volatile" variable.


--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

James Kanze

unread,
Apr 7, 1997, 3:00:00 AM4/7/97
to

da...@henry.ece.cmu.edu (David A. Cuthbert) writes:

But in a function declaration, there is no code to alter the object.

|> Borland 5.2
|> complains (IMHO rightfully) about the following:
|>
|> int testfunc(int const h) {
|> ++h;
|> return h;
|> }
|>
|> with the error message "Cannot modify a const object."

Certainly. This is a definition, not a declaration. The external
declaration of this function can be:

int testfunct( int ) ;

or

int testfunct( int const ) ;

regardless of the definition.

|> Now, having said that, there uses of such code are few and far
|> between. I might use it if I suspected a library of altering
|> something it shouldn't be (and that something is a parameter passed
by
|> value to my function). Adding const ensures that my code isn't the
|> one affecting the parameter (aside from the shady area of const_cast
|> and his thug friends :-).

Adding const in the declaration doesn't ensure anything. The only place
where const is significant is in the definition.

|> {The point is that, in the above function parameter, there is
|> no reason for "const" to appear in the function declaration,
|> where it is only confusing/misleading for client coders. -hps}

--

James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85
62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14
54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles
France
-- Conseils en informatique industrielle --

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Herb Sutter

unread,
Apr 8, 1997, 3:00:00 AM4/8/97
to

BAD MSG:
<pgpmoose.1997...@isolde.mti.sgi.com>
-Newsreader: Forte Agent 1.0/32.390
X-Auth: PGPMoose V1.1 PGP comp.std.c++
iQBVAwUBM0kxLEy4NqrwXLNJAQErOwH/VO2zBGJLMm3E+s+iL5A7XA8jSefUB/if
9Dd8d+bWL03AC7UQ53O1m4vpPZokVsHnMopXBPz+KlSR3FhXXtjnwg==
=TdwQ

Valentin Bonnard <bonn...@pratique.fr> wrote:


>Herb Sutter wrote:
>> [Warning: This solution contains points that some may find arguable.]
>

>Yes


>
>> 1. Since the point object is passed by value, there is little
>> or no benefit to declaring it const.
>>
>> > Point GetPoint( const int i ) { return points_[i]; }
>>
>> 2. Same comment about the parameter as above. Normally const
>> pass-by-value is unuseful and misleading at best.
>

>Has argued, it's arguable.

Actually, this wasn't one of the points I considered arguable. :-) Although
there has been some discussion about it in clcm, I believe the consensus is
that the above advice stands for function declarations.

>> 4. (Arguable.) Return-by-value should normally be const for
>> non-builtin return types. This assists client code by
>> making sure the compiler emits an error if the caller
>> tries to modify the temporary (for example,
>> "poly.GetPoint(i) = Point(2,2);"...
>

>which isn't possible anyway so the argument doesn't hold
>the way it's written.

Why not? If GetPoint() returns a non-const temporary, that's legal code.
Consider:

struct Point { Point( int, int ) {} };

Point f() { return Point(1,1); }
const Point g() { return Point(1,1); }

int main()
{
f() = Point(2,2); // OK
g() = Point(2,2); // error
}

>[The std question:]
>Why is operator= a special case in this respect ?
>
> T () = T ();
>
>isn't valid (as far as I know), but
>
> T () += T ()
>
>is. Is there a rationnal for the introduction of such
>a special case ?

I don't follow. Both operator= and operator+= should always return a
reference to *this. However, operator+ (for example) knows it is returning a
temporary and therefore should return a const temporary to let the compiler
emit an error if the user tries to modify it.

>> after all, if this was
>> intended to work, GetPoint() should have used
>> return-by-reference and not return-by-value in the first
>

>The user of the class can want to use the temporary directly.
>I don't like the idea taht it's const (I don't like the way
>C++ define that anyway).

Can you give an example?

---
Herb Sutter (mailto:he...@cntc.com)

Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824

---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Robert O'Dowd

unread,
Apr 9, 1997, 3:00:00 AM4/9/97
to

Alexandre Oliva wrote:

> Oleg Zabluda writes:
> > Herb Sutter <he...@cntc.com> wrote:
> > : > void AddPoint( const Point pt ) { InvalidateArea();

> > : > points_.push_back(pt); }
>
> > : 1. Since the point object is passed by value, there is little
> > : or no benefit to declaring it const.
>
> > : > Point GetPoint( const int i ) { return points_[i]; }
>
> > : 2. Same comment about the parameter as above. Normally const
> > : pass-by-value is unuseful and misleading at best.
>
> > I don't understand both those comments. First of all, I don't see what
> > harm does it make to declare formal arguments as const. I don't see
> > how it can be misleading. The difference between formal and actual
> > arguments is almost the very the first thing a programmer learns.
>
> A formal argument is never const, as it seems to be in the former
> examples. However, the actual argument the callee gets will be, which
> does not mean the caller should consider the argument it passes on is
> unchanged.

In this example (argument is const int i), the const adds exactly
nothing to any promise made to a calling function. C (and by extension
C++) passes all arguments by value, meaning that the function can't
change the value it receives.

A const qualifier on function parameters only has meaning if the
argument
is a reference or a pointer. It means that the entity *being
referenced*
(or with a pointer, being pointed at) does not change. For example,
look at the allowable lines in the following function.

void Foo(const int *x)
{
*x = 2; // invalid, changing the object pointed to by x

++x; // valid because x is a pointer that has been passed by
value
}

> [More snipped]

- <Automagically appended trailer>
Robert O'Dowd Phone : +61 9 553 3618
DSTO Bldg A51, Fax : +61 9 553 3577
HMAS Stirling E-mail : Robert...@dsto.defence.gov.au
Rockingham, Western Australia, 6958

Disclaimer: Opinions above are MINE and may be worth what you paid for
them.

Alexandre Oliva

unread,
Apr 13, 1997, 3:00:00 AM4/13/97
to

Robert O'Dowd writes:

> Alexandre Oliva wrote:

>> A formal argument is never const, as it seems to be in the former
>> examples. However, the actual argument the callee gets will be, which
>> does not mean the caller should consider the argument it passes on is
>> unchanged.

> In this example (argument is const int i), the const adds exactly
> nothing to any promise made to a calling function. C (and by extension
> C++) passes all arguments by value, meaning that the function can't
> change the value it receives.

Any function can change the value of any argument it receives, unless
it is declared const. This does not imply that the function can
change the value from which is argument was copied.

void foo(/*const*/ int i) {
++i; // i contains 4 now
}
void bar() {
int j = 3;
foo(j); // but j remains 3
}

However, if i was declared const, the statement ++i would be invalid
inside the body of foo.

> A const qualifier on function parameters only has meaning if the
> argument is a reference or a pointer.

You mean the const qualifier has meaning for the caller only if the
argument if a reference or a pointer. As I expect to have shown now
(this has been my point all the time), a const qualifier in a formal
argument does prevent changes of the function argument (not of the
value it was copied from) inside the function body, but has no visible
effect outside the function body. In fact,

void foo(int i);
void foo(const int i);

are both declarations of the same function, that is, foo is not
overloaded in this case, and the type of foo is a function that
takes an int as argument and does not return anything. But,

void bar(int *i);
void bar(int *const i); // same as void bar(int *i)
void bar(const int *i);
void bar(const int *const i); // save as void bar(const int *i)

declare two different overloaded functions, the first one is a
function that takes a pointer to int, and the other takes a pointer to
const int.

--
Alexandre Oliva
mailto:ol...@dcc.unicamp.br mailto:aol...@acm.org
Universidade Estadual de Campinas, SP, Brasil

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

0 new messages