ACCU Home page ACCU Conference Page ACCU 2017 Conference Registration Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Google+ ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pineditor << letters;

Overload Journal #34 - Oct 1999 + Programming Topics + Letters to the Editor   Author: Aaron Ridout

This is a little defensive-coding idea I have been thinking about for a few moments and I discussed it briefly with Detlef Vollmann at a recent conference, he suggested the temporary const copy that may be optimised away...

What happened to me was that I was calling 'fn(int)' which was changed (unbeknownst to me) to 'fn(int &)'! The function specifically wanted to change the object passed to it.

for( int i = 0 ; i != 10 ; ++i )
{
  fn(i) ;
}

Which was my code before and after the new library including 'fn', but after the silent change, this failed, randomly as the User reported, at run-time. So I now need a good method to protect myself from such silent changes.

What I want to achieve is that if I write a loop (for example a for loop) such that any code in the body cannot corrupt the loop index. One could write:

for( int i = 0 ; i != 10 ; ++i )
{
  const int ci(i) ;
  fn(ci) ;
}

Thus creating a new variable, initialised from the loop index, but can be passed by reference to a function (the compiler may optimise this temporary away?), but it would still be possible to make a mistake and pass the original index rather than the const version. The compiler now can warn me if someone wants to subvert my index. Ideally what I'd like to write is:

for( const mutable int i = 0 ; i != 10 ; ++i )
{
  fn(i) ;
}

Now the compiler knows that the loop index is const within the body of the loop, except that the loop construct wants mutable access to the index.

According to 'The C++ Programming Language' 3rd edition, this is syntactically ok. What I'd like changed in the standard is that mutable could apply to all variables, not just objects of class-scope. I would welcome your thoughts…

Sean Corfield responds: This is a 'common' problem in that if a by-value function is replaced by a pair (typically) of const & and non-const & functions then any code you have that calls the function with a non-const lvalue will 'suddenly' call the non-const version. Of course, library vendors shouldn't pull this sort of stunt in any way that would change the semantics of a call. In other words, if the following code is guaranteed to work in one version of the library, the vendor should not change this in a subsequent release of the library:

T x( y );
fn( y );
assert( x == y );

What this basically shows is that adding a non-const& overload to existing code is almost always a bad idea - if you intend to maintain the semantics (the implied 'assert' above) then why accept a *non-const* parameter at all?

Yes, syntactically this is ok, but there are additional constraints beyond the syntax - 'mutable' may only be applied to class member data declarations.

The key issue here is that you want to ensure the correct overload is chosen. A variant of the extra constant copy above that would ensure the correct overload is chosen without introducing a new variable is:

for ( int i = 0; i!= 10; ++i )
{
  const int& ci = i;      // use ref instead of copy
  fn( ci );
}

Another possibility is simply to add a cast to the call:

for ( int i = 0; i!= 10; ++i )
{
  fn( const_cast<const int&>( i ) );
}

But this looks ugly and doesn't work well with multiple calls ­ I'm just giving it as an example!

Overload Journal #34 - Oct 1999 + Programming Topics + Letters to the Editor