I'm currently working on cleaning up an API full of fu开发者_运维百科nction templates, and had a strong desire to write the following code.
template <typename T, typename U, typename V>
void doWork(const T& arg1, const U& arg2, V* optionalArg = 0);
When I invoke this template, I would like to do so as follows.
std::string text("hello");
doWork(100, 20.0, &text);
doWork('a', text); // oops!
doWork<char, std::string, void>('a', text); // to verbose!
Unfortunately, the second invocation doesn't compile since the compiler cannot deduce the type of the optional parameter. This is unfortunate, since I really don't care what the parameter type is, but rather that its value is NULL. Also, I'd like to avoid the route of the third invocation since it hampers readability.
This lead me to try to make the template argument V
have a default type, which also doesn't work since you cannot apply a default type to a function template argument (at least using VC++ 9.0).
template <typename T, typename U, typename V = void> // oops!
void doWork(const T& arg1, const U& arg2, V* optionalArg = 0);
My only remaining option is to introduce an overload of doWork
that knows nothing of the template argument V
.
template <typename T, typename U>
void doWork(const T& arg1, const U& arg2)
{
doWork(arg1, arg2, 0);
}
template <typename T, typename U, typename V>
void doWork(const T& arg1, const U& arg2, V* optionalArg);
Is this the best approach to solving this problem? The only drawback I see is that I could potentially introduce many trivial forwarding functions if a function template contains many parameters that have suitable defaults.
I think that your forwarding function is a perfectly suitable solution, although in your solution, won't you have to explicitly specify the template parameters? (0 is an integer constant that can be coverted to any V*
type.) Also doWord
vs doWork
?
As a general rule, try to avoid optional parameters where they don't have a very strong pay-off.
It might be easier to force clients of you function to just add a , (void*)0
if appriopriate than add to much extra mechanism to support both a two parameter and a three parameter version of the template. It depends on the expected uses, though.
From the client code's point of view, if it doesn't have a third parameter, why does it need to invent one?
So if you aim for usability and readability, I agree with your wrapper method: it makes perfect sense, and the wrapper you wrote is responsible for a decent value of the third parameter you necessitated.
On top of that, it makes it possible to use a different default for different specializations, if needed.
One possibility is to reorder the template arguments, so the optional one comes first.
template <typename V, typename T, typename U>
void doWork(const T& arg1, const U& arg2, V* optionalArg = 0);
doWork<void>('a', text);
Forwarding looks OK too.
But it seems default arguments and templates don't match well, though.
精彩评论