为另一种模板类型声明模板特化的正确方法是啥?

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&lt;Bits&gt;的模板声明了一个新的重载,而不是特化。

但这并不重要,因为像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++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译