正如 litb 所说,ADL 在它可以工作的地方是优越的,这基本上是每当模板参数可以从调用参数中推导出来的:
#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());
}
输出:
ADL works!
3
No ADL!
5
Overloading+ADL 可以通过部分专门arithmetic::mul
化S = ns::Identity
. 但它确实依赖于调用者以允许 ADL 的方式调用它,这就是为什么你从不std::swap
显式调用的原因。
所以问题是,您希望您的库的用户必须将您的函数模板部分专门用于什么?如果他们要将它们专门用于类型(通常是算法模板的情况),请使用 ADL。如果他们打算将它们专门用于整数模板参数,如您的示例中那样,那么我想您必须委托给一个类。但我通常不期望第三方定义乘以 3 应该做什么——我的库会做所有的整数。我可以合理地期望第三方来定义乘以八进制数会做什么。
想一想,求幂对我来说可能是一个更好的例子,因为 myarithmetic::mul
与 令人困惑地相似operator*
,所以实际上没有必要专门mul
研究我的例子。然后我会为第一个参数专门化/ADL-overload,因为“身份的力量就是身份”。不过,希望你能明白这一点。
我认为 ADL 有一个缺点——它有效地扁平化了命名空间。如果我想使用 ADL 来为我的班级“实施” arithmetic::sub
,sandwich::sub
那么我可能会遇到麻烦。我不知道专家对此有什么看法。
我的意思是:
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
}
}
现在,我有一个类型ns::HeapOfHam
。我想利用 std::swap 风格的 ADL 来编写我自己的算术::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);
}
}
我还想利用 std::swap 样式的 ADL 来编写我自己的三明治::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.
}
}
等一下。我不能那样做,可以吗?不同命名空间中的两个不同函数具有相同的参数和不同的返回类型:通常不是问题,这就是命名空间的用途。但我不能对它们都进行 ADL 化。可能我错过了一些非常明显的东西。
顺便说一句,在这种情况下,我可以完全专门化每个arithmetic::sub
和sandwich::sub
。调用者会using
选择其中一个,并获得正确的功能。但是,最初的问题是关于部分专业化,所以我们可以假装专业化不是一种选择,而我实际上没有将 HeapOfHam 设为类模板吗?