为另一种模板类型声明模板特化的正确方法是啥?
Posted
技术标签:
【中文标题】为另一种模板类型声明模板特化的正确方法是啥?【英文标题】:What is the proper way to declare a specialization of a template for another template type?为另一种模板类型声明模板特化的正确方法是什么? 【发布时间】:2010-04-28 02:10:06 【问题描述】:模板函数特化的通常定义是这样的:
class Foo
[...]
;
namespace std
template<>
void swap(Foo& left, Foo& right)
[...]
// namespace std
但是,当专门化的类型本身就是模板时,如何正确定义专门化?这是我得到的:
template <size_t Bits>
class fixed
[...]
;
namespace std
template<size_t Bits>
void swap(fixed<Bits>& left, fixed<Bits>& right)
[...]
// namespace std
这是声明swap
的正确方式吗?它应该是模板函数std::swap
的特化,但我不知道编译器是否这样看待它,或者它是否认为它是它的重载或其他什么。
【问题讨论】:
对不起,我相信你在这个上弄错了。命名空间 std 中明确允许模板函数的特化。 你是对的“程序可以将任何标准库模板的模板特化添加到命名空间 std。”谢谢你让我检查。 【参考方案1】:您的解决方案不是模板特化,而是 std 命名空间中函数的重载,根据 c++ 标准,这是“未定义的行为”。
This question 正是你的问题。
Scott Meyers 在 Effective C++ 中讨论了这个问题,usenet's comp.lang.c++ 上有一个后续线程。
他建议你在fixed自己的命名空间中定义它。 确保“固定”位于命名空间中。 不要在调用前加上 'std::'。 让Koenig (a.k.a. argument dependent) lookup 找到正确的交换函数。如果您在尝试在命名空间 std 中定义它时看到编译错误,这可能是由于您不幸选择了类名 :) 当在 namespace std
中“固定”被视为 std::fixed
时,浮点精度运算符。
【讨论】:
实际名称是fixed_integer
,我为这个问题缩短了它。但是如果我在不同的命名空间中声明它,std
命名空间中的所有函数不会调用正确的函数吗?而且std::swap
的大多数用法不是指定std
命名空间吗?
添加了描述交换用例的 ADL 的***链接。 Koenig 查找表示对非命名空间限定的 swap(N::arg1, N::arg2) 的调用将使用参数的命名空间来确定比 std::swap 更好的拟合。我见过的大多数代码都没有明确调用 std::swap。更喜欢“使用 std::swap”的方法。
@Head Geek:std lib 实现应该使用 Koenig 查找 std::swap()
,因此它们会选择用户定义的重载。不过,我不确定他们会这样做。【参考方案2】:
函数模板的部分特化是不允许的,所以fixed<Bits>
的模板声明了一个新的重载,而不是特化。
但这并不重要,因为像std::swap(a, b)
这样的调用只会选择最佳匹配函数。它不要求该函数是通用std::swap
模板的特化。如果最佳匹配函数是重载而不是特化,则将调用重载。
【讨论】:
【参考方案3】:从句法上讲,您的“专业化”是正确完成的。然而,正如其他人已经指出的那样,它并不是真正的部分特化,而是函数重载。函数模板不支持偏特化。
值得注意的是,语言规范禁止向命名空间std
添加任何内容(参见17.4.3.1/1)。我理解你为什么坚持在std
做这件事,因为你试图部分专业化std::swap
。由于您的 swap
确实是重载而不是专业化,因此您可以在 std
之外安全地执行此操作,在包含 yor fixed
的同一命名空间内。依赖于参数的名称查找(“Koenig 查找”)应确保在应用于 fixed
类型的参数时通过名称查找找到您的 swap
(除非调用代码明确引用 std::swap
而不仅仅是 swap
)。
【讨论】:
【参考方案4】:不能使用直到运行时才确定的值来声明模板参数。要将数字用于模板参数,它必须是“const”,以便可以在编译时解析:
template <const size_t Bits>
class fixed
[...]
;
namespace std
template<const size_t Bits>
void swap(fixed<Bits>& left, fixed<Bits>& right)
[...]
// namespace std
【讨论】:
嗯?模板参数必须是编译时常量,但在没有const
的情况下声明它从来没有任何问题。甚至没有警告。以上是关于为另一种模板类型声明模板特化的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
C++,2参数类模板的部分特化:无法将函数定义与现有声明匹配
C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译
C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译