Christopher Smith on 16 May 2003 22:46:01 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[ALACPP] Chris' stupid C++ trick of the week


So, this is a little off the core purpose of this list, but I thought
I'd share a C++ tidbit.

I find that if I work on C++ for a week, I will invariably screw up
something fairly subtle. I also find that if I share my mistake with
others, many are surprised by the mistake, and learn something from the
experience.

So, my hope is that this little e-mail will serve the same purpose for
the members of the list.

So, I was writing some template code, and I did (simplified):

template<typename T>
T& doFoo(T& aTarget) {
    //do something with aTarget
    return aTarget;
}

Something bothered me about this, but I couldn't put my finger on it.
This seemed very similar to what the iostreams libraries do, so I
convinced myself that nothing could go wrong.

Then my code started not working on certain compilers when optimization
flags were turned on. This jogged something in my mind, so I looked more
closely at the code. After a lot of thinking, I came up with a specific
example that was more familiar:

const Bar& doFoo(const Bar& aBar) {
    //do something with aBar
    return aBar;
}

I don't know about you but I find it much easier to identify bad code
when I don't have generic types abstracting things for me. So, I
remembered that the above example was definitely bad, but I couldn't
remember *why* it was bad (this was made more complicated by additional
code in the function). I mean, it looks very safe and clean right? It
took me about 5 minutes to realize the problem:

The object referenced by aBar may be destroyed when you exit the doFoo()
block (and obviously before the caller gets a chance to look at it).
How? Well, the compiler actually has the option of passing arguments to
doFoo() by value. Of course this is only in the special case of a
parameter passed as a ref to a const.

The trick here is the templated code does a good job of hiding this
problem from casual examination. So, my next thought was how to program
defensively so that compiler errors will get thrown where appropriate.
So far I've come up with four solutions, neither of which are ideal:

1) Use compile time assertions to ensure that the template is not
instantiated on const types.

2) Make sure Bar's copy constructor is private, and better still with
pure virtual functions which will cause a linker error in the event that
you get by the privacy restriction.

3) Use a typelist to limit the types which my generic function can take
to a select set of non-const types.

4) Use a compiler that always inlines templated function calls. ;-)

All 4 approaches have their draw backs. I'd love to hear suggestions of
other approaches or opinions on this issue in general.

-- 
Christopher Smith <x@xxxxxxxx>

P.S.: As it turned out, this was not why my code was failing, but I was
glad for the actual bug merely for helping me to find this esoteric
error that would normally take me weeks to figure out.
_______________________________________________
alacpp mailing list
alacpp@xxxxxxxxxxx
http://lists.ellipsis.cx/mailman/listinfo/alacpp