Fully transitive const is not necessary
Sean Kelly
sean at invisibleduck.org
Wed Apr 2 15:47:38 PDT 2008
== Quote from Walter Bright (newshound1 at digitalmars.com)'s article
> Sean Kelly wrote:
> > I agree that transitive const can achieve more demonstrably correct code
> > but I don't think it follows that this will necessarily improve productivity.
> > My experience with const in C++ is that it actually reduces productivity
> > because I spend more time dealing with the misapplication of const than
> > I gain from whatever bugs the application of const may have prevented.
> > In fact, in all my time as a programmer I can't think of a single instance
> > where I've encountered a bug that could have been prevented through
> > the use of const.
> C++ const is more of a suggestion than anything verifiable by the
> compiler and static analysis.
I know you like to talk about the unreliability of const in C++ because of
const_cast and the like, but in my opinion those are theoretical objections
because I've never encountered even a single use of const_cast in actual
code. So while I do agree that C++ const may not be useful for static
analysis, I also believe that C++ const works just fine for achieving more
demonstrably correct code. For example, if I create this class:
class BoxedInt
{
int val;
public:
BoxedInt( int v ) : val( v ) {}
int get() const { return val; }
void set( int v ) { val = v; }
};
I am confident that if someone tries to call set() on a const instance of
BoxedInt a compile-time error will occur. To me, this means that C++
const helps to write demonstrably correct code because the compiler
will trap at least some logic errors related to intended program behavior.
Sure, the user could theoretically cast away const-ness of his BoxedInt
object and thus break everything, but as far as I'm concerned that's his
problem.
> This will become more obvious as C++ tries
> to compete in the multiprogramming arena. The C++ compiler *cannot* help
> with multiprogramming issues; the C++ programmer is entirely dependent
> on visual inspection of the code, making C++ a tedious, labor intensive
> language for multiprogramming.
I agree with this. C++ const is intended to catch logical mistakes and is not
useful for multiprogramming.
> How many times have you looked at the documentation for a function API,
> and wondered which of its parameters were input and which were output?
Never. Though it perhaps helps a bit that C++ classes are value types.
I know that counter-examples could be provided along the lines of:
struct C
{
char* buf;
};
void fn( const C val )
{
val.buf[0] = 0;
}
But I've never seen anyone actually make this kind of mistake. Perhaps I've
just been lucky.
> When you talk about the "misapplication" of const, does that mean
> failure to understand what the specific API of a function is?
Most often, the problem I've seen is for const to not be consistently applied
to class member functions or within the program itself. It's been a while so
I'm actually fuzzy on what the exact problems were, but I remember modifying
a bunch of unrelated code when I created a new member function in some class.
If I had to guess I'd say this had something to do with the non-transitive nature
of const in C++ and that the class contained pointers which were being used in
questionable ways.
In fact, here's one such example that I know I encountered, though it may not
be the exact situation I'm recalling above. It does involve some minor refactoring
so it's not exactly a "misapplication" of const however:
class C
{
D* d;
E* e;
public:
C () : d ( new D ), e( new E ) {}
D* getD() const { assert( d ); return d; }
E* getE() const { assert( e ); return e; }
};
class D
{
public:
char* getName() const { ... }
int getAge() { ... }
};
void useC( C const& c )
{
C c;
D* d = c.getD();
assert( d );
printf( "%s %d\n", d->getName(), d->getAge() );
}
I wanted to add a new get method to this class but noticed that, based on the
class' design there was no reason for it to return pointers. The contained objects
were owned by C and they were guaranteed to be non-null, so why not let the
class interface reflect that. In exchange the compiler would catch some stupid
mistakes on the user side and the user would be able to do away with their
null checks. So:
class C
{
D* d;
E* e;
F* f;
public:
C () : d ( new D ), e( new E ), f( new F ) {}
D const& getD() const { return *d; }
E const& getE() const { return *e; }
F const& getF() const { return *f; }
};
The obvious problem being that the class is now returning const references
where it could previously return pointers to non-const data. This was entirely
fine within the context of what was actually being done, but the methods in D,
E, and F weren't labeled as const in a consistent manner. So I basically ended
up having to change code all over the place because C++ does not have
transitive const. Obviously, the above issue won't exist in D however, and
the other situations I can't recall may be quite similar to this and thus a non-
issue in D. I wish I could recall more exactly.
Sean
More information about the Digitalmars-d
mailing list