2

我正在阅读一本关于 C++ 的书,由于某种原因,我似乎无法理解函数的模板专业化(隐式模板实例化、显式模板实例化和显式专业化)。

需要明确的是,当一个人可以声明/定义一个非模板函数时,我不理解显式模板实例化或显式特化的需要,这将覆盖通用模板函数和特化。

您将在何处、何时以及为什么使用显式模板实例化和/或显式的函数特化?

4

2 回答 2

2

根据我的经验,在现代 C++ 中,很少进行显式函数模板特化。

首先,因为它在许多方面与模板类专业化完全不同,而习惯于模板类专业化(这种情况更为常见)的程序员将会颠倒他们的直觉。

其次,因为函数重载、模板模式匹配和显式模板特化都会对调用的代码产生影响,而且这三者往往同时发生。函数重载和模板模式匹配一​​开始就足够复杂:显式模板特化增加了另一层复杂性,使您的代码更难理解。它必须证明这一成本是合理的。

第三,因为自从语言发明以来,我们学到了更强大的技术。我们可以标记分派,分派到特征类,或使用 SFINAE 在模板重载之间进行选择。在他们之间,他们几乎可以做任何显式专业化可以做的事情,而且往往更多。

我可以向您展示我在代码中使用它的示例。我有一个方法 ,GetField可以得到一个Cow,DuckOtter. 我可以公开GetCow,GetDuckGetOtter,但通常与这 3 个一起使用的代码是对称的。通过制作它GetField<Cow>,我可以在动物的选择上使用相同的代码模板。

然后我专门GetField针对这 3 种类型。现在询问任何其他类型都会产生链接器错误,我得到了我喜欢的统一语法。三种类型的实现GetField不同,但使用是统一的。

如果我今天要写它,我会改成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将在沼泽标准函数中而不是在模板专业化中。在这种情况下,模板功能只是一点界面光泽。

在特征类型、标签调度和重载的强大功能之间,我还没有遇到过模板函数显式特化是多年来最好的解决方案的情况,当我重新审视过去使用它的问题时,我通常会重新考虑并使用其他技巧。

于 2015-07-22T13:53:20.537 回答
2

这很有用的一个领域是,当您只想通过不同的返回类型来重载函数时。普通函数不支持此功能,因为编译器无法解决调用哪个变体的歧义。但是使用模板功能,您可以实现这一点

template<typename T> 
T func() 
{
    ...
}

template<>
std::string func()
{
    ...
}

template<>
bool func()
{
    ...
}
于 2015-07-22T13:02:29.983 回答