General algorithms like sorting or searching are usually expressed as templates. Since many of them should allow the users to specify how certain things are to be done (for instance comparing two values), they usually take a function-template as argument:
This is all nice and we can even use existing functions:
That is, if the passed function is not overloaded. The above example will fail harshly:
$ clang++ -Wall -Wextra -Wpedantic -std=c++1y main.cpp main.cpp:12:2: error: no matching function for call to 'transform' std::transform(vec1.begin(), vec1.end(), vec2.begin(), std::back_inserter(vec3)... ^~~~~~~~~~~~~~ /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_algo.h:4189:5: note: candidate template ignored: couldn't infer template argument '_BinaryOperation' transform(_InputIterator1 __first1, _InputIterator1 __last1, ^ /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_algo.h:4152:5: note: candidate function template not viable: requires 4 arguments, but 5 were provided transform(_InputIterator __first, _InputIterator __last, ^ 1 error generated.
Now, this is very specific user-code (there are no templates anywhere on the user-side) and we know exactly which overload we want to call. Even the compiler would soon find out, if it actually tried.
The usual solution here is a lambda:
This is ridiculous: In order to pass a normal global function-template we have to write an explicit wrapper-lambda around it that is more than four times longer and doesn’t even use the exactly correct types (
const int&). It would actually be a bug if we needed to preserve the return-type.
The other usual solutions (passing the template-argument explicitly and using
static_cast) have even more problems, so let’s just ignore them here.
Let’s look at the general case: We get an arbitrary number of arguments of arbitrary types that we want to use to call one function of an overload-set that will return an arbitrary type.
Assuming that the function is called “fun”, the lambda to achieve that heroic act looks like this: 1
This uses quite a few advanced language-feature that many C++-programmers are probably happy to avoid: Variadic templates, perfect-forwarding,
decltype(auto) as explicit lambda-return-type. But hey, it is correct! We just needed 83 characters of difficult to understand (for normal people) boilerplate to pass a function whose name is three letters long!
This is embarrassing! To get the behavior that we should get by default,2 we have to write so much code. Personally I also hate the idea that, as far as I see the situation, we cannot get better by throwing even more templates at the problem (which usually helps quite a lot).
This leaves us with a tool that we should almost always try to avoid: The preprocessor.
As terrifying it is, as much as it is against everything that modern C++ is about, it seems to be the least bad solution in this case:
Two notes about this macro:
__VA_ARGS__and I mentioned here, that I was not 100% sure that those should be there and whether this could make some valid code ill-formed. As it was pointed out on reddit, this prevents ADL which is probably not what we want here, so I removed them.
__VA_ARGS__, since the name of the function could include a template with more than one argument (think of static member-function-templates), and those will only work like this (The preprocessor would consider the comma that divides the template-arguments as a comma that separates macro-arguments).
After that we are now able to write our code like this:
Which is certainly better then the other alternatives I’ve seen.
Yes, this really works for all types, including
void: In C++ it is legal to return the “result” of a function that returns
void if our function returns
void too. This is not a problem at all but actually really awesome.↩
To answer any “if you want something in the language, write a proposal”: I am a student and don’t have the money to visit standardization meetings nor the experience to write down a feature like this without help from someone experienced. If you find me someone who would mentor me and champion the proposal for the committee, I would very seriously consider writing it.↩