Consider the following function template:
template<typename T> void Foo(T)
{
// ...
}
Pass-by-value semantics make sense if T
happens to be an integral type, or at least a type that's cheap to copy.
Using pass-by-[const]-reference semantics, on the other hand, makes more sense if T
happens to be an expensive type to copy.
Let's assume for a second that you are writing a library. Ideally, as a library implementer, your job is to provide your consumers with a clean API that is both as generic and efficient as possible. How then, do you provide a generic interface that caters to both types of argument passing strategies?
Here is my first attempt at getting this to work:
#include <boost/type_traits.hpp>
template<typename T> struct DefaultCondition
{
enum {value = boost::is_integral<T>::value /* && <other trait(s)> */};
};
template< typename T, class Condition = DefaultCondition<T> > class Select
{
template<bool PassByValue = Condition::value, class Dummy = void> struct Resolve
{
typedef T type;
};
template<class Dummy> struct Resolve<false, Dummy>
{
typedef const T& type;
};
public: typedef typename Resolve<>::type type;
};
Typical usage:
template<typename T> class EnterpriseyObject
{
typedef typename Select<T>::type type;
public: explicit EnterpriseyObject(type)
{
// ...
}
};
struct CustomType {};
void Usage()
{
EnterpriseyObject<int>(0); // Pass-by-value.
(EnterpriseyObject<CustomType>(CustomType())); // Pass-by-const-reference.
}
This, of course, indirectly breaks implicit template argument deduction for non-class templates:
template<typename T> void Foo(typename Select<T>::type)
{
// ...
}
void Usage()
{
Foo(0); // Incomplete.
Foo<int>(0); // Fine.
}
This can be "fixed" with the Boost.Typeof
library and a macro, a la the WinAPI
:
#define Foo(Arg) ::Foo<BOOST_TYPEOF((Arg))>((Arg))
Though this is just a quasi-portable hack.
As you can see, my general approach is not really satisfactory for all cases.
As a hobbyist programmer, I neither have real-world experience nor do I have access to production-quality code for reference. I also realize that this might seem like a bad case of premature optimization, but I'm genuinely interested in a couple of things:
- Do you, or have you used this type of optimization* in the past?
- Does the
Boost
(or any other public) library already provide similar functionality? - If the answer to #1 or #2 is a 'yes' -- how is the non-class template case handled?
- Are there any obvious pitfalls that I'm not seeing with something like this?
- Finally, is this even a sane thing to do?
* Not profiled. ;)