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