As litb says, ADL is superior where it can work, which is basically whenever the template parameters can be deduced from the call parameters:
#include <iostream>
namespace arithmetic {
template <class T, class S>
T mul(const T& x, const S& y) { return x * y; }
}
namespace ns {
class Identity {};
// this is how we write a special mul
template <class T>
T mul(const T& x, const Identity&) {
std::cout << "ADL works!\n";
return x;
}
// this is just for illustration, so that the default mul compiles
int operator*(int x, const Identity&) {
std::cout << "No ADL!\n";
return x;
}
}
int main() {
using arithmetic::mul;
std::cout << mul(3, ns::Identity()) << "\n";
std::cout << arithmetic::mul(5, ns::Identity());
}
Output:
ADL works!
3
No ADL!
5
Overloading+ADL achieves what you would have achieved by partially specializing the function template arithmetic::mul
for S = ns::Identity
. But it does rely on the caller to call it in a way which allows ADL, which is why you never call std::swap
explicitly.
So the question is, what do you expect users of your library to have to partially specialize your function templates for? If they're going to specialize them for types (as is normally the case with algorithm templates), use ADL. If they're going to specialize them for integer template parameters, as in your example, then I guess you have to delegate to a class. But I don't normally expect a third party to define what multiplication by 3 should do - my library will do all the integers. I could reasonably expect a third party to define what multiplication by an octonion will do.
Come to think of it, exponentiation might have been a better example for me to use, since my arithmetic::mul
is confusingly similar to operator*
, so there's no actual need to specialize mul
in my example. Then I'd specialize/ADL-overload for the first parameter, since "Identity to the power of anything is Identity". Hopefully you get the idea, though.
I think there is a downside to ADL - it effectively flattens namespaces. If I want to use ADL to "implement" both arithmetic::sub
and sandwich::sub
for my class, then I could be in trouble. I don't know what the experts have to say about that.
By which I mean:
namespace arithmetic {
// subtraction, returns the difference of lhs and rhs
template<typename T>
const T sub(const T&lhs, const T&rhs) { return lhs - rhs; }
}
namespace sandwich {
// sandwich factory, returns a baguette containing lhs and rhs
template<typename SandwichFilling>
const Baguette sub(const SandwichFilling&lhs, const SandwichFilling&rhs) {
// does something or other
}
}
Now, I have a type ns::HeapOfHam
. I want to take advantage of std::swap-style ADL to write my own implementation of arithmetic::sub:
namespace ns {
HeapOfHam sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
assert(lhs.size >= rhs.size && "No such thing as negative ham!");
return HeapOfHam(lhs.size - rhs.size);
}
}
I also want to take advantage of std::swap-style ADL to write my own implementation of sandwich::sub:
namespace ns {
const sandwich::Baguette sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
// create a baguette, and put *two* heaps of ham in it, more efficiently
// than the default implementation could because of some special
// property of heaps of ham.
}
}
Hang on a minute. I can't do that, can I? Two different functions in different namespaces with the same parameters and different return types: not usually a problem, that's what namespaces are for. But I can't ADL-ify them both. Possibly I'm missing something really obvious.
Btw, in this case I could just fully specialize each of arithmetic::sub
and sandwich::sub
. Callers would using
one or the other, and get the right function. The original question talks about partial specialization, though, so can we pretend that specialization is not an option, without me actually making HeapOfHam a class template?
fun<U, N>(u)
, so you cannot overload (N
does not appear anyhere in the parameter). But i think if "overloading" is possible, it's the preferred way. Excellent example isstd::swap
orstd::begin
orstd::end
(where the latter two are C++0x functions). Notice that Sutter's article was written 9 years ago. Not sure whether he still recommends to do it the "delegate to class" way. And i think it doesn't scale nicely: Won't work with ADL - you will have to fiddle with all sort of namespaces and specialize their templates. No nice – Mutt