I have common code – Dijkstra's algorithm – I use in different contexts, so I decided to use tag dispatch.
The common code is in the following function (you can see End
get dispatched depending on the Tag template parameter):
template <typename Tag, typename ... Args>
void Dijkstra(blahblah, Args&&... arg) {
...
if (End(Tag(), cost, n_id, distances, time_limit, args ...)) {
break;
}
For most of the contexts I define a default no-op as follows:
template<typename ... Args>
bool inline End(Args&& ...) {
return false;
}
For one context I define the function with the following signature:
bool inline End(OneContextTag, Duration d, NodeId n_id, Distances distances, Du time_limit, blahblah) {
Everything worked as expected, till I found I forgot &
in the signature after Distances
– I was copying Distances, a large unordered_map, every time.
However, after I changed it to const Distances&
to avoid expensive copying, the less specialized noop version got called. I have no idea why. And how to fix it.
(I swear the change is only in adding a single character &
. Or const&
)
(The signature is otherwise correct, if I comment out the generic noop version, it just uses the OneContextTag
version.)
(The code is more complex, but I hope it can be figured out from this.)
I do not have an answer to why overload resolution works the way it does here atm. But I have a potential solution for you, which is also (IMO) more robust:
Change the default End
to accept a UseDefaultEnd
tag as the first parameter. For each context which should use the default End
, subclass its tag from UseDefaultEnd
:
#include <iostream>
struct UseDefaultEnd {};
/* Comment first parameter; then you get the same behavior as
you're currently trying to solve. */
template<typename ... Args>
bool inline End(UseDefaultEnd, Args&& ...) {
// bool inline End(Args&& ...) {
return false;
}
struct OneTag {};
struct OtherTag : public UseDefaultEnd {};
bool inline End(OneTag, int const & i) {
return true;
}
template<typename Tag>
void Caller() {
int i = 42;
if (End(Tag(), i)) {
std::cout << "Used specific version of End" << std::endl;
}
}
int main() {
Caller<OtherTag>();
std::cout << "---" << std::endl;
Caller<OneTag>();
}