我正在读一本关于C ++的书,由于某种原因,我似乎无法理解函数的模板专业化(隐式模板实例化,显式模板实例化和显式专业化)。
要明确的是,当我们只能声明/定义一个非模板函数时,我不理解显式模板实例化或显式特化的需要,这将覆盖通用模板函数和特化。
在何处,何时以及为什么要使用显式模板实例化和/或函数的显式特化?
答案 0 :(得分:2)
这个有用的一个方面是你想要通过不同的返回类型重载函数。普通函数不支持这一点,因为编译器无法解决调用哪个变量的模糊性。但是使用模板功能,你可以实现这个
template<typename T>
T func()
{
...
}
template<>
std::string func()
{
...
}
template<>
bool func()
{
...
}
答案 1 :(得分:2)
根据我的经验,在现代C ++中,很少进行显式函数模板专业化。
首先,因为它在许多方面与模板类专业化的工作方式完全不同,而习惯于模板类专业化(更常见)的程序员将会使他们的直觉颠倒过来。
其次,因为函数重载,模板模式匹配和显式模板特化都会影响所调用的代码,并且所有3都倾向于在同一点发生。函数重载和模板模式匹配非常复杂,可以从以下开始:显式模板特化增加了另一层复杂性,使您的代码更难理解。它必须证明这个成本是合理的。
第三,因为自语言发明以来我们学到了更多强大的技巧。我们可以标记调度,调度到特征类,或使用SFINAE在模板重载之间进行选择。在他们之间,他们可以做几乎所有明确的专业化可以做的事情,而且往往更多。
我可以向您展示我在代码中使用它的示例。我有一个方法GetField
,可以获得Cow
,Duck
或Otter
。我可以公开GetCow
,GetDuck
和GetOtter
,但通常使用这3个代码的代码是对称的。通过制作GetField<Cow>
,我可以在选择动物时使用相同的代码。
然后我将GetField
专门用于那3种类型。现在要求任何其他类型生成链接器错误,我得到我喜欢的统一语法。 GetField
的实现对于3种类型是不同的,但使用是统一的。
如果我今天要写,我会改为GetField
这样:
// a template for passing types as tags:
template<class T> struct tag{using type=T;};
// Type-specific getters:
Cow& GetField( tag<Cow> ) { /* impl */ }
Duck& GetField( tag<Duck> ) { /* impl */ }
Otter& GetField( tag<Otter> ) { /* impl */ }
// Template getter for "any" type:
template<class T>
auto GetField()->
decltype( GetField( tag<T>{} ) )
{
return GetField( tag<T>{} );
}
我会得到更好的错误消息(没有重载而不是链接器错误),而GetField
的实现将是bog标准函数而不是模板特化。在这种情况下,模板功能只是一点界面光泽。
在traits类型的功能,标签调度和重载之间,我没有遇到模板函数显式特化是多年来最好的解决方案的情况,当我重新审视过去使用它的问题时,我通常会重新考虑并使用其他技术。