arlo on 17 May 2003 03:15:01 -0000 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
Re: [ALACPP] Chris' stupid C++ trick of the week |
{ Aaccidentally sent my response to Chris directly, not to the list. My bad. Here you all go. Sorry to give you two copies, Chris. } > So, I was writing some template code, and I did (simplified): > > template<typename T> > T& doFoo(T& aTarget) { > //do something with aTarget > return aTarget; > } > > 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: > > ... Two other solutions pop immediately to mind: function overloading and type computers. Both assume that what you really want the compiler to generate is: if T is non-const: Bar& doFoo(Bar &bar) { /* ... */ } if T is const: const Bar doFoo(const Bar &bar) { /* ... */ } Note the return value by value for the const case. The reason for this is the same as the reason that the optimization is allowed by the standard (see Chris' discussion of same). We are basically forcing all compilers to make the optimization on the return value, thus allowing us to treat them all uniformly (and in ways that we expect). So, the ugly (but easy to think of) way to get this is overloading: template< typename ArgT > ArgT& doStuff(ArgT &arg) { /* #1 ... */ } template< typename ArgT > ArgT doStuff(const ArgT &arg) { /* #2 ... */ } The language requires that the compiler detect the overload, and use the most specialized template instantiation that applies to the given arg. Thus, this would result in one function being called for const args, and the other for non-const ones. The problem is that #1 and #2 are the same code, and must be repeated. This can be done by copy and paste or by having the preprocessor copy and paste for you, but we are assuming that the work done is intricately related to the value returned, so that it can't be factored out. Thus, this solution is unsatisfactory. A better one is as follows: // --- in params.h /* * Assumes compiler supports tempalte partial specialization. * Type computers can also be written without making this * assumption, but they take about 15 additional boilerplate * lines, and that explanation isn't pertinent to the task * at hand. If people want to see it, start another thread * and say so. */ template< typename ArgT > class RetT { typedef ArgT value; }; template< typename ArgT > class RetT< const ArgT > { typedef const ArgT value; }; // --- in your code #include <params.h> // ... template< typename ArgT > RetT< ArgT >::value doStuff(ArgT &arg) { /* ... */ } Actually, the better version is: template< typename ArgT > RetT< ArgT >::value doStuff(ParamT< ArgT >::value arg) { /* ... */ } , because this gets around a number of problems with reference args, and explicitly allows template specialization to override all of your function passing behavior for types that should always be by value (eg, your function call counters or stack depth counters). In fact, this second approach is the one that Boost takes. Check out their call_traits library (http://www.boost.org/libs/utility/call_traits.htm) for their implementation. They are generally trying to solve a similar problem (allowing the function to declare that it takes a const ref, without worrying about whether T is instantiated with a const type). Their solution happens to also address the question at hand, so I thought that I'd bring it up. Oh, and if you don't want the copy semmantics of a return value for your type, you can either 1) specialize the type computer for your type, or 2) have the type computer use boost::ref (see their utility lib) to wrap a reference in a value type (syntax of a reference, semmantics of a non-owning dumb pointer): #include <boost/ref.hpp> template< typename ArgT > class RetT< const ArgT > { typedef boost::ref< const ArgT > value; }; Arlo _______________________________________________ alacpp mailing list alacpp@xxxxxxxxxxx http://lists.ellipsis.cx/mailman/listinfo/alacpp