.--------------------------------------------------------------------. | 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!
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.
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.
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!
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.)
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++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
: 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.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> 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 ?
> > 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.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
>: 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.
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++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
Oleg Zabluda <zabl...@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.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
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
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> Oleg Zabluda <zabl...@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
>>: 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
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> : 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.
|> Oleg Zabluda <zabl...@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 --
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> ...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.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
James Kanze <james-albert.ka...@vx.cit.alcatel.fr> wrote:
> wil...@interlog.com (Steve Willer) writes: >> Oleg Zabluda <zabl...@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
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
[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.
[ Send an empty e-mail to c++-h...@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++-requ...@ncar.ucar.edu ]
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);
} > 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 :-)
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:
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(); }
David A. Cuthbert (da...@henry.ece.cmu.edu) wrote: : James Kanze <james-albert.ka...@vx.cit.alcatel.fr> wrote: : > wil...@interlog.com (Steve Willer) writes:
: >> Oleg Zabluda <zabl...@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:
: 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
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
Marcelo Cantos <marc...@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++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
da...@henry.ece.cmu.edu (David A. Cuthbert) writes:
|> James Kanze <james-albert.ka...@vx.cit.alcatel.fr> wrote: |> > wil...@interlog.com (Steve Willer) writes: |> |> >> Oleg Zabluda <zabl...@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.
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++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
BAD MSG: <pgpmoose.199704061722.16...@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 <bonna...@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).
> > : 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
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++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]