【问题标题】:Template specialization vs. Function overloading模板特化与函数重载
【发布时间】:2019-09-17 21:27:45
【问题描述】:

这个问题很简单。有什么区别:

template <typename T>
T add(T a, T b)
{
    return a + b;
}

template <>
int add<int>(int a, int b)
{
    return a + b; //no reason to specialize, but still...
}

还有:

template <typename T>
T add(T a, T b)
{
    return a + b;
}

int add(int a, int b)
{
    return a + b; //no reason to overload, but still...
}

它们似乎是一样的。

【问题讨论】:

  • 我自己也不是很确定,如果两个 add 定义在同一个命名空间中,第二个是否会因歧义而引发编译器错误?
  • @Romen 不确定标准是怎么说的,但在 GCC 上,专业化和重载都可以正常编译。
  • 我在 Visual Studio 上试用过。当您在第二个版本中调用 int a = add(1,2); 时,不会引发编译器错误,但 Visual Studio 会使用非模板版本。在第一个版本中,它显然使用了专门的案例。所以我认为它们是一样的!
  • @Romen 在 GCC 上相同。这种模棱两可实际上让我问了这个问题
  • @DarkAtom 歧义通过以下规则解决:如果模板与非模板的签名完全匹配,则非模板在重载决议中获胜。

标签: c++


【解决方案1】:

除了重载决议如何进行的差异外,模板特化将您锁定在签名中。这意味着你不能这样做:

template <>
long add<int>(int a, int b)
// ^- the return type is long , not int
{
    return a + b; //no reason to specialize, but still...
}

编译器会抱怨返回类型的改变,因为声明(和函数签名)是由主模板决定的。编译器在进行重载解析时甚至不考虑专业化。它只是从主实例中实例化一个声明,并且该声明必须与它最终调用的内容相匹配。

因此,重载的定义方式更加灵活。并且编译器没有歧义。如果参数类型匹配,则重载决议机制偏爱非模板而不是模板生成的函数。重载通常比专门化函数模板更容易。

【讨论】:

  • 实际上可以通过使用专门的类型特征模板类来做到这一点,例如 template&lt;typename T&gt; struct result { using type = T; }; 专用于 int case template&lt;&gt; struct result&lt;int&gt; { using type = long; }; 作为返回类型。单独更改特定 T 的返回类型甚至不需要为函数编写专门化。
  • @VTT - 是的。这只是使 C++ 过于专家友好。与简单的重载相比,除非我别无选择,否则我认为我永远不会提出 trait。
【解决方案2】:

Fist 变体更简单,因为编译器必须收集然后从仅包含单个项目 add&lt;int&gt;(int, int) 特化的候选集中进行选择。而第二种变体将导致编译器通过收集一组包含两个项目的候选者来执行一些额外的工作 - add&lt;int&gt;(int, int)add(int, int) 并使用花哨的函数重载排名算法在它们之间进行选择。

【讨论】:

    【解决方案3】:

    在这种情况下没有区别,但在某些情况下会。让我们看看这个示例代码

    template<class T> // (a) 
    void f(T);
    
    template<>        // (b)
    void f<>(int*);
    
    template<class T> // (c)
    void f(T*);
    
    int main()
    {
        int *p; 
        f(p);
    }
    

    当我们调用f(p); 时,您希望调用什么函数?大多数人会选择b,他们会错的。这样做的原因是因为特化不适用于重载决议。它们是模板的特殊配方,它告诉编译器,如果您推断出这种类型,则改为以这种方式删除模板。

    所以编译器所做的是通过重载决议并说我可以调用aTint*,或者我可以调用cTint。由于后者更专业T 更窄),c 获胜。这可能非常令人惊讶,但是一旦您忽略专业化并仅考虑重载,它就会很有意义。

    Walter E. Brown 有一个关于模板和专业化的非常深入的视频,名为 “C++ Function Templates: How Do They Really Work?”,您应该考虑观看。时长一个小时,但是很详细。

    【讨论】:

    • “在这种情况下没有区别”。实际上,add(42, 4.2f).
    【解决方案4】:

    在“简单”情况下,它们的行为相同,但在某些情况下它们会有所不同。

    一个微不足道的区别是显式调用 add&lt;int&gt; 将调用专门化而不是重载。

    重载决议还有其他区别,例如:

    add(42, 4.2f);
    

    将因模板特化而失败(intfloat,因此无法推断出 T

    而重载是有效的,会将float 转换为int

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      相关资源
      最近更新 更多