Pet*_*der 12 c++ partial-specialization non-member-functions
正如大多数C++程序员应该知道的那样,不允许对自由函数进行部分模板特化.例如,以下是非法的C++:
template <class T, int N>
T mul(const T& x) { return x * N; }
template <class T>
T mul<T, 0>(const T& x) { return T(0); }
// error: function template partial specialization ‘mul<T, 0>’ is not allowed
Run Code Online (Sandbox Code Playgroud)
然而,类/结构的模板偏特是允许的,并且可以被利用来模仿的自由功能模板偏特的功能.例如,最后一个示例中的目标目标可以通过以下方式实现:
template <class T, int N>
struct mul_impl
{
static T fun(const T& x) { return x * N; }
};
template <class T>
struct mul_impl<T, 0>
{
static T fun(const T& x) { return T(0); }
};
template <class T, int N>
T mul(const T& x)
{
return mul_impl<T, N>::fun(x);
}
Run Code Online (Sandbox Code Playgroud)
它更笨重,更简洁,但它完成了工作 - 就用户mul而言,他们获得了所需的部分专业化.
我的问题是:在编写模板化的自由函数(打算由其他人使用)时,你应该自动将实现委托给类的静态方法函数,这样你的库的用户可以随意实现部分特化,或者做你只是以正常的方式写出模板化的函数,并且生活在人们将无法专门化它们的事实中?
正如 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());
}
Run Code Online (Sandbox Code Playgroud)
输出:
ADL works!
3
No ADL!
5
Run Code Online (Sandbox Code Playgroud)
重载+ADL 实现了通过部分专门arithmetic::mul化S = ns::Identity. 但它确实依赖于调用者以允许 ADL 的方式调用它,这就是为什么您从不std::swap显式调用的原因。
所以问题是,您希望库的用户必须部分专门化您的函数模板来做什么?如果他们要将它们专门用于类型(通常是算法模板的情况),请使用 ADL。如果他们打算将它们专门用于整数模板参数(如您的示例所示),那么我想您必须委托给一个类。但我通常不期望第三方定义乘以 3 应该做什么 - 我的库将执行所有整数。我可以合理地期望第三方定义八元数乘法的作用。
想想看,求幂可能是我使用的更好的例子,因为 myarithmetic::mul与 令人困惑地相似operator*,所以实际上没有必要专门研究mul我的例子。然后我会专门/ADL 重载第一个参数,因为“任何事物的力量的身份就是身份”。不过,希望你明白这个想法。
我认为 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
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我有一个类型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);
}
}
Run Code Online (Sandbox Code Playgroud)
我还想利用 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.
}
}
Run Code Online (Sandbox Code Playgroud)
等一下。我不能那样做,可以吗?不同命名空间中的两个不同函数具有相同的参数和不同的返回类型:通常不是问题,这就是命名空间的用途。但我无法将它们都 ADL 化。可能我错过了一些非常明显的东西。
顺便说一句,在这种情况下,我可以完全专门化arithmetic::sub和 中的每一个sandwich::sub。调用者可以选择using其中之一,并获得正确的功能。不过,最初的问题讨论的是部分专业化,那么我们是否可以假装专业化不是一种选择,而无需我实际将 HeapOfHam 作为类模板?
| 归档时间: |
|
| 查看次数: |
4377 次 |
| 最近记录: |