译自Function Templates Partial Specialization in C++ 为什么C ++不允许对函数模板进行部分专业化?
考虑一下,template specialization 归结为为给定类型选择正确的实现。并且解析发生在编译时。 现在将其与函数重载进行比较:它包括为给定类型的参数选择正确的函数。并且解析也发生在编译时。 从这个角度来看,这两个功能看起来非常相似。因此,通过函数重载可以达到与函数模板(部分或全部)专业化等效的功能,这是正常的。让我们用一个例子来说明。
考虑以下模板函数f:
template <typename T> void f(T const& x) { // body of f }假设当T为std::string时,我们需要特定的实现。 我们可以专门研究f:
template <> void f<std::string>(std::string const& x) { // body of f for std::string }或者我们可以只是重载
void f(std::string const& x) { // body of f for std::string }无论哪种方式,当您传递字符串时,执行都会通过特定的实现。
partial specialization 也是如此。假设我们想要vector的f的特定实现。我们不能使用partial specialization来编写它,因为以下内容是非法的:
// Imaginary C++ template <typename T> void f<std::vector<T>>(std::vector<T> const& v) { // body of f or vectors }但是我们可以通过重载实现
template <typename T> void f(T const& x) // #1 { // body of f } template <typename T> void f(std::vector<T> const& v) // #2 { // body of f for vectors } f(std::vector<int>{}); // this call goes to #2而且我们也获得了预期的效果。
这是否意味着不存在对模板函数进行部分专业化的情况?没有!在某些情况下,超载不可以实现。
重载适用于模板参数,可以说是占据了模板函数用例的相当一部分。但是,如果模板不在参数中怎么办?例如,它可以是函数的返回类型:
模板<typename T> T f(int i,std::string s) { // ... }甚至在函数原型中都不存在:
template <typename T> void f() { // ... }这些情况之间的共同点是,必须在调用的位置显式地指定模板类型:
f<std::string>();在这种情况下,重载是没有用处的,因此对于这种情况,我认为函数模板的Partial Specialization是适用的。Let’s review our options for working around the fact that C++ does not support it natively.
重载他是西蒙·布兰德(Simon Brand)提出的技术。 它包括添加一个参数,该参数携带有关T类型是什么的信息。 这个参数,类型,只是带有另一个类型T:
template <typename T> struct type{};(我们也可以在此处省略名称T,因为它并没有使用在模板的主体中。) 这可以返回到我们可以使用重载而不是Partial Specialization的情况。
请考虑以下示例。 我们想要设计一个模板函数create,该函数返回默认初始化的T类型的对象:
return T();除了要返回的类型是向量时,在这种情况下,我们希望分配1000的容量以备重复的插入:
std::vector<T> v; v.reserve(1000); return v;因此,我们需要一个默认的实现,并其中一个对所有T都使用一个vector 。换句话说,我们需要使用vector <T>来partially specialize f。 示例
template <typename T> struct type{}; template <typename T> T create(type<T>) { return T(); } template <typename T> std::vector<T> create(type<std::vector<T>>) { std::vector<T> v; v.reserve(1000); return v; } template <typename T> T create() { return create(type<T>{}); }即使我们不能对函数模板做Partial Specialization,但可以对类模板做。 有一种方法可以通过重用后者来实现。
当对一组函数的多个实现使用一组重载时,我们可以了解发生了什么。
当您对一组函数的多个实现使用一组(全部)Partial Specialization时,我们也可以了解正在发生的事情。 但是,当将重载和专业化功能混合在一起使用时,您将进入魔术,伏地魔和百慕大三角生活的领域,一个事物以一种无法解释的方式表现的世界,在这个世界上,您最好不知道太多的解释,因为它们可能会使您陷入困境,并且您的大脑最终会进入墓地,并被刺穿黑暗魔法的尖刺刺穿。😃 为了说明这一点,请考虑一下/u/sphere991给出的有见地的示例,它说明了一切:
template <typename T> void f(T ); // #1 template <typename T> void f(T* ); // #2 template <> void f<>(int* ); // #3 f((int*)0); // calls #3但是:
template <typename T> void f(T ); // #1 template <> void f<>(int* ); // #3 template <typename T> void f(T* ); // #2 f((int*)0); // calls #2 !!重载和专业化声明的唯一顺序决定了调用的行为。 阅读这段代码会使我不寒而栗。😦 无论选择哪种技术,都不要将函数重载和Partial Specialization混为一谈。
我们已经了解了何时需要对功能模板进行Partial Specialization以及如何对其进行仿真。 但是我们还没有回答我们最初的问题:为什么C ++不允许对函数模板进行Partial Specialization? 总结一下是 :该功能已被考虑了一段时间,并被忽略了,因为concept_maps可以代替它。 但是concept_maps是在后面的版本中也被放弃了!
我这样理解: 允许在C ++中对函数模板进行Partial Specialization这没有错,但是我们不知道是否有一天会使用它。