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
arithmetic::mul
Overloading + ADLは、の関数テンプレートを部分的に特殊化することで達成できることを実現しますS = ns::Identity
。ただし、ADLを許可する方法で呼び出すことは呼び出し元に依存しているため、std::swap
明示的に呼び出すことはありません。
つまり、問題は、ライブラリのユーザーが関数テンプレートを部分的に特殊化する必要があると予想することです。タイプに特化する場合(アルゴリズムテンプレートの場合のように)、ADLを使用します。あなたの例のように、それらを整数テンプレートパラメータに特化する場合は、クラスに委任する必要があると思います。しかし、私は通常、3による乗算が何をすべきかをサードパーティが定義することを期待していません-私のライブラリはすべての整数を実行します。八元数による乗算が何をするかを第三者が定義することを合理的に期待できます。
考えてみると、私arithmetic::mul
はと紛らわしいほど似ているので、べき乗は私が使用するためのより良い例だったかもしれません。したがって、実際に私の例operator*
に特化する必要はありません。mul
次に、「何かの力に対するアイデンティティはアイデンティティである」ため、最初のパラメータに特化/ADLオーバーロードします。うまくいけば、あなたはアイデアを得るでしょう。
ADLには欠点があると思います。名前空間を効果的にフラット化します。arithmetic::sub
ADLを使用して、クラスとクラスの両方を「実装」したい場合は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を利用して、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);
}
}
また、std :: swapスタイルのADLを利用して、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.
}
}
ちょっと待ってください。できませんね。同じパラメータと異なるリターンタイプを持つ異なる名前空間の2つの異なる関数:通常は問題ではありません。それが名前空間の目的です。しかし、両方をADL化することはできません。おそらく私は本当に明白な何かを見逃しています。
ところで、この場合、私はそれぞれとを完全に専門化することができarithmetic::sub
ますsandwich::sub
。発信者はusing
どちらか一方になり、適切な機能を取得します。ただし、元の質問は部分的な特殊化について説明しているので、実際にHeapOfHamをクラステンプレートにしない限り、特殊化はオプションではないふりをすることができますか?