【问题标题】:function templates overloading versus fully-specializing函数模板重载与完全专业化
【发布时间】:2021-12-11 20:51:45
【问题描述】:

你好,我有这个例子:

#include <iostream>

template <typename T>
int compare(T const&, T const&);

template <typename T>
int compare(T*, T*);

template <>
int compare(char const * const&, char const* const&);

template <unsigned N, unsigned M>
int compare(char const (&)[N], char const(&)[M]);

template <unsigned N>
int compare(char const (&)[N], char const(&)[N]);


template <typename T>
int compare(T const& lhs, T const& rhs){
    std::cout << "compare(T const&, T const&)\n";

    if(std::less<T>()(lhs, rhs))
        return -1;
    if(std::less<T>()(rhs, lhs))
        return 1;
    return 0;
}

template <typename T>
int compare(T* p1, T* p2){
    std::cout << "compare(T*, T*)\n";
    if( std::less<T>()(*p1, *p2) )
        return -1;
    if( std::less<T>()(*p2, *p1) )
        return 1;
    return 0;
}

template <>
int compare(char const * const& p1, char const* const& p2){
    std::cout << "compare(char const * const &, char const * const &)\n";
    return strcmp(p1, p2);
}

template <unsigned N, unsigned M>
int compare(char const (&ra)[N], char const(&rb)[M]){
    std::cout << "compare(char const(&)[N], char const(&)[M])\n";
    return strcmp(ra, rb);
}

template <unsigned N>
int compare(char const (&ra)[N], char const(&rb)[N]){
    std::cout << "compare(char const(&)[N], char const(&)[N])\n";
    return strcmp(ra, rb);
}

int main(){

    int a = 10, b = 57;
    char const* const cp1 = "Hello";
    char const* const cp2 = "World";

    std::cout << compare(a, b) << '\n';
    std::cout << compare(&a, &b) << '\n';
    std::cout << compare(cp1, cp2) << '\n';
    // std::cout << compare("Hi", "Hi") << '\n'; // error: ambiguous
    // std::cout << compare("Hi", "World!") << '\n'; // error: ambiguous

    cout << '\n';
}
  • 为什么调用compare传递两个常量字符数组即使是不同长度的数组也是模棱两可的?

  • 事实上,具有单个非类型参数N 的第二个重载只是为了与 C++ 11 兼容,因为在 C++14 及更高版本上我可以使用std::enable_if。请

  • 为什么还要传递compare(cp1, cp2); 调用compare(T*, T*) 而不是compare(char const* const&amp;, char const * const&amp;)

  • 最后一个问题:我在这里声明函数模板的顺序是否会影响首选函数?谢谢。

更新:

我已经设法通过仅更改版本的签名来使其工作,该版本的签名采用两个指向参数类型的指针:compare(T*, T*) 到:

template <typename T>
int compare(T const * const&, T const* const&);

现在它工作正常,你能解释一下原因吗?

【问题讨论】:

  • 但是你真的真的应该限制你的帖子关于一个问题的一个问题。主题指南对此非常明确。
  • 离题:指针变体不应该也接受 const 指针吗 (T const*)?
  • 哦,顺便说一句,有一种方法可以更好地编写一般比较:return std::less(rhs, lhs) - std::less(lhs, rhs);(给false - true = 0 - 1 = -1 on lhs &lt; rhs, 1 - 0&gt;0 - 0 上平等)。

标签: c++ overloading template-specialization function-templates


【解决方案1】:

为什么比较传递两个常量字符数组的调用即使是不同长度的数组也是模棱两可的?

因为两者

template <>
int compare(T*, T*);

template <unsigned N, unsigned M>
int compare(char const (&)[N], char const(&)[M]);

在这两种情况下都匹配,在第二种情况下,第三个重载也加入进来(您可能已经专门化为 template &lt;unsigned N&gt; compare&lt;N, N&gt;(...) 以避免至少在后两者之间产生歧义。

为什么还要传递 compare(cp1, cp2);调用 compare(T*, T*) 而不是 compare(char const* const&, char const * const&)?

在选择正确的函数模板时,首先会发生重载决议,并且只有在选择正确的重载时才会考虑模板特化。您现在提供compare 函数的两个模板重载:

template <typename T>
int compare(T const&, T const&);

template <typename T>
int compare(T*, T*);

这两个中,指针模板匹配更好,所以选择了这个。

template <>
int compare(char const * const&, char const* const&);

是被丢弃的重载的特化,因此之前已经作为候选者被淘汰。 1)

通过按值接受指针 (char const* px),您将专门化 other 基本模板,因此会看到所需的结果。

我在这里声明函数模板的顺序会影响首选函数吗?

没有。所有在调用函数时已知的重载都会被考虑,无论它们以何种顺序被声明。如果不是这种情况,则几乎不可能(至少仅通过大量研究工作)预测如果通过不同的标头导入(可能是间接地!)哪个重载会被调用。

编辑(考虑更新的问题):

template <typename T>
int compare(T const&, T const&);

template <typename T>
int compare(T const * const&, T const* const&);

template <>
int compare(char const * const&, char const* const&);

char const* 仍然比T const&amp; 匹配更好的T const* const&amp;,所以仍然会选择第二个重载。如果您现在比较所涉及的签名,那么您的更改会引发,但是,(实际上未更改的)特化获得了第二个重载的特化(char const* 更接近 T const* const&amp; 而不是 T const&amp;,就像在重载解决过程中一样)。

由于现在特化指的是已经选择的重载,因此您现在可以获得所需/预期的结果。

旁注:保留原始基本模板 (T*) 并将专业化更改为

template <>
int compare(char const*, char const*);

也会引发更接近于第二次重载的专业化——而且这种变化会更具决定性,因为没有理由通过 const 引用接受指针——如果无论如何都没有优化,这只是另一个(不必要) 间接级别(对于非常量指针而言并非如此,在这种情况下,您可以对传递给函数本身的指针变量应用更改)。

1)取自Frank的回答,同时被删除。

【讨论】:

  • 我已经编辑了这个问题。你要付支票吗?
  • @ItachiUchiwa 这样做了...
【解决方案2】:

问题 1

为什么比较传递两个常量字符数组的调用即使是不同长度的数组也是模棱两可的?

回答 1

这是因为当你写compare("Hi", "World!"); 时,有两个版本可以使用(同样好)。第一个版本是

template <typename T>
int compare(T*, T*);

第二个版本是:

template <unsigned N, unsigned M>
int compare(char const (&ra)[N], char const(&rb)[M]);

重要提示

注意版本:

template <>
int compare(char const * const& p1, char const* const& p2);

是一个特化,所以它不参与重载。更具体的记住

特化实例化模板;他们不会超载它。因此,特化不会影响函数匹配。

问题 2

为什么还要传递 compare(cp1, cp2);调用 compare(T*, T*) 而不是 compare(char const* const&, char const * const&)?

答案 2

这是因为版本:

template <typename T>
int compare(T*, T*);

将被选择用于“所有指针”,更重要的是它可以在版本中参与重载

template <>
int compare(char const * const& p1, char const* const& p2);

是一种特化,因此不参与重载。

问题 3

我在这里声明函数模板的顺序会影响首选函数吗?

答案 3

没有

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2016-09-12
    • 2020-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多