As you're probably aware, function templates in C++ don't support default arguments, which is to say that:
template < typename T = float >
T add(const T &lhs, const T &rhs) { return lhs + rhs; }
doesn't compile, because we're attempting to say "by default, assume a float." This, of course, extends to member functions as well.
In the instance above, we could simply allow template type inference to do its thing. However, in some code I've recently written, the parameter that I'm using for said inference is being used for inference alone -- which means I'm instantiating the whole object, passing it to my function, and then only taking its type.
template < typename derived_type >
void some_action(int x, int y, int z, derived_type type)
{
// the derived_type instance 'type' is never used, only its type 'derived_type'
base_type * item = new derived_type();
}
If the type has a trivial constructor, this isn't a deal-breaker, but since it's client-facing code I cannot guarantee this will always be the case. It also seems foolish to require client types to provide a special, trivial constructor as a work-around. Furthermore, passing an instance of the object may leave clients with the impression that data/values are being taken from the instance, which isn't the case. Finally, trivial constructor or not, the potential overhead, both run-time and in writing the trivial constructors, is not in the spirit of C++ or the greater codebase.
I'd rather just avoid the whole can of worms.
Foregoing template type inference would cleanly solve all of these issues, albeit with slightly different syntax:
// Using template type inference:
some_action(1, 2, 3, some_derived_type());
// W/out template type inference:
some_action<some_derived_type>(1, 2, 3);
Now we're providing only the type, as we really wanted in the first place, with no risk of expensive constructors, undue work-arounds, or misunderstandings. The last hurdle is that it's kind of inconvenient for clients to have to specify the derived_type template function specifically -- especially when it its going to be the same predictable thing 90% of the time (I should mention now that some_action is, in fact, a member function of 'some_class' which itself takes, among others, 'base_type' as a template argument):
template < /* some other arguments /* typename base_type >
class some_class
{
public:
template < typename derived_type >
some_action(int x, int y, int z);
private:
/* blah blah */
};
As you can see, giving 'derived_type' a default value of 'base_type' would neatly have given me the flexibility to specify any derived type, while also giving me a convenient syntax for the common case:
// instead of some_action<base_type>(1, 2, 3):
some_action(1, 2, 3);
As a work around, I implemented a non-template version of some_action, having the same return type and taking the same parameters as the template version, which simply forwards the call to the template version, but specifying 'base_type' as the template argument:
void some_action(int x, int y, int z) { some_action<base_type>(x, y, z); }
This gives me the syntax I want, but having two functions with what appears to be the same signature (aside from the template specification on one, which I realize makes the signatures different, or at least sufficiently distinguishable to the compiler, in reality) gave me a bit of an uneasy feeling.
On the other hand, this type of work-around seems like it would be common practice, I even daresay an honest-to-goodness idiom -- but, before I move forward, I just wanted to appeal the the C++ gurus here to ask whether this is as good as it appears to be.
So, Gurus, I ask you: is this standard-conforming C++, or are some compilers going to complain about a duplicated function signature? Is there anything in this which might cause problems in some unforeseen way? It seems to work under Visual C++ 2005 professional, but I don't have access to other platforms at the moment.
Also, does anyone have any clue as to the rationale for C++ not supporting default arguments to template parameters on functions?