Xref: utzoo comp.lang.c++:10307 comp.std.c++:410 Path: utzoo!attcan!uunet!nih-csl!lhc!adm!cmcl2!yale!cs.utexas.edu!radar!cadillac!vaughan@mcc.com From: vaughan@mcc.com (Paul Vaughan) Newsgroups: comp.lang.c++,comp.std.c++ Subject: Re: const is not object-oriented Summary: I disagree Message-ID: <13050@cadillac.CAD.MCC.COM> Date: 10 Nov 90 20:24:51 GMT References: <1990Nov9.181408.23110@odin.corp.sgi.com> Sender: news@cadillac.CAD.MCC.COM Reply-To: vaughan@mcc.com (Paul Vaughan) Followup-To: comp.lang.c++ Organization: MCC VLSI CAD Program Lines: 176 In-reply-to: linton@sgi.com (Mark Linton) From: linton@sgi.com (Mark Linton) Newsgroups: comp.lang.c++,comp.std.c++ Date: 9 Nov 90 18:14:08 GMT Reply-To: linton@sgi.com (Mark Linton) Organization: sgi Lines: 78 The "const" keyword in C++ specifies a property of storage, and as such is not particularly useful in an object-oriented program. Hm, const is also used to declare properties of pointers. Having a const IntVector* p; doesn't imply that whatever p points to can't be changed. It simply means that it can't be changed using p. My impression is that const really only specifies a property of storage in the declaration of a file scoped object. Even then, I'm not too sure. Declaring a parameter or a member function as const requires knowledge about the implementation of a class. Consider a class IntVector with a member function sum: class IntVector { public: IntVector(unsigned int size); ~IntVector(); int get(unsigned int i); // return element i void set(unsigned int i, int v);// set element i to v int sum(); private: int* data; unsigned nelements; } The function "sum" is to return the add up all the elements in the vector. One might think it would be natural to define sum as a const member function, which would allow a call to sum on a const IntVector object (such as a const IntVector parameter). After all, computing the sum of the elements of a vectors doesn't "change" the vector. Suppose, however, that sum will be called many times before the vector is modified. Because you know the vector is only modified by set, you could add two members to the private section: int current_sum; boolean sum_is_valid; The set member function would set sum_is_valid to false, and sum would check the flag to determine whether it needs to compute the sum or simply use the previously computed value. This is indeed a problem, but it can be overecome. My usual approach to the problem is to use a non-const overloading and a cast, like this: int IntVector::sum() { //add em up, save it, and return }; int IntVector::sum() const { return ((IntVector*) this)->sum(); } If you implement this caching strategy, you can no longer make sum a const member function! The great irony is that you could define set as a const member function because it modifies data pointed at by the vector object, not the vector's own structure. Sure. C++ can't really recognize that an object considers certain things that its members point to as being part of itself. This being the case, another approach to the above problem would be to declare int* current_sum; as a member variable and allocate/delete the space for it in the constructor/destructor. But this seems pretty silly. The bottom line here is that the compiler can't tell you what should or should not be const, it can only tell you what cannot be const. So, I claim that const is a pretty useless concept in class interfaces because it depends on the class implementation. I can see the use of const for concrete types (const char*), or perhaps in very special cases for optimization (like for helping a vectorizing compiler handle a parallel program). It need not depend on the class implementation, as shown above. While I've generally accepted the idea that declaring things const is useful for several theoretical reasons, I'd still like to see some empirical evidence. It certainly is not without cost. There is one place where the notion of consts confuses compiler writers. Cfront, for example, generates a warning if you pass a temporary to a non-const ref parameter. For example, class A { public: A(int); int f(A&); }; A* a; a->f(A(4)); This call will generate a warning from cfront. I believe this warning is out-right wrong because it will confuse programmers into believing that they should change the declaration of f to "int f(const A&)". However, if the function f is implemented using caching, they can't do it. So the code must be split up: A tmp(4); a->f(tmp); The above code generates a syntax error using cfront simply because it is a fragment. Also, I'm not sure it is quite the fragment that you intended. For instance, this code (which does compile) issues only the warning shown with cfront: class A { public: A(int); int f(A&); }; main() { A* a; a->f(A(4)); //warning: a used but not set } I agree that if cfront had produced a non-const warning, it would have been wrong. I'm using the cfront 2.0 derivative Sun CC. Perhaps the version you are using is broken? I'll discuss the following code. class A { public: A(int); int f(A&); }; main() { const A* a; a->f(A(4)); // generates a const warning } This is precisely a case where I would use class A { public: A(int); int f(A&); int f(const A&); // implemented in terms of f(A&), using a cast }; if the f member function doesn't change the observable state of an A object. The bottom line is if you are defining a C++ class, especially for a library, do not use const parameters or const member functions. Const means storage, not behavior, so it is by definition an implementation/representation concept. I disagree. If you are a compiler writer, do not generate this bogus warning when a temporary is passed to a non-const ref parameter. As far as I can tell, there is no problem here. Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639 Box 200195, Austin, TX 78720 | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan