7

这是C++ Primer,第 4 版,第 16 章中的一个示例,它是关于模板专业化的。

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

template <class T>
int compare(const T& v1, const T& v2) {
    if(v1<v2) return -1;
    if(v2<v1) return 1;
    return 0;
}

template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
    return strcmp(v1,v2);
}

int main(int argc, const char *argv[])
{
    cout << compare("abc","defg") << endl;
    return 0;
}

我希望compare("abc","defg")会调用模板的专用版本。但事实是,g++ 4.6.3 不会编译这段代码并给出以下错误:

错误:没有用于调用“比较(const char [4],const char [5])”的匹配函数

注意:候选是:模板 int compare(const T&, const T&)

现在给出以下事实:

I. 字符串字面量,或 C++ 中的 C 风格字符串实际上是一个const char array

二、如果作为普通的非引用类型传递,数组将被悄悄地转换为指向其第一个元素的指针。

在这里,我只是将字符串文字“abc”和“defg”作为对 的引用传递 const char*,我希望它们会先转换为const char*,然后通过引用传递。但似乎 g++ 不同意我的观点,拒绝编译代码。

但是如果我用函数重载替换模板特化,也就是替换

template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
    return strcmp(v1,v2);
}

int compare(const char* const& v1, const char* const& v2){
    return strcmp(v1,v2);
}

然后 g++ 会很乐意编译它。

那么问题到底出在哪里呢?为什么我不能 const char* const&在模板专业化版本中按参数类型传递字符串文字?

4

2 回答 2

6

下面的答案基于来自的解释C++ Templates: The complete guide pp57: Using String literals as Arguments for Function templates

template <class T>
int compare(const T& v1, const T& v2) {
    if(v1<v2) return -1;
    if(v2<v1) return 1;
    return 0;
}

这要求两个参数v1v2具有相同的类型。

template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
    return strcmp(v1,v2);
}

这要求您具有带const char *类型的参数。

但是,"abc"has typechar const[4]"defg"has type char const[5]。它们是不同的类型。由于专用版本和模板版本都需要引用参数,因此在参数推导期间没有数组到指针的衰减。因此,您不能将不同长度的字符串文字传递给它们以找到匹配项。如果您提供一个不需要任何参数推导的常规函数​​,编译器将找到匹配项。

如果您声明非引用参数,您可以用不同长度的字符串文字替换它们。这种行为的原因是,在参数推导array-to-pointer转换(通常称为衰减)期间,仅当参数没有引用类型时才会发生。

于 2013-05-27T15:49:16.767 回答
4

模板特化不参与重载解决过程。重载决议只考虑主模板。

模板特化仅在其主要模板“赢得”重载决议时才发挥作用。即模板特化用于特化过程中顾名思义),它们在重载决议期间是完全不可见的。

出于这个原因,在您的第一个示例中,重载决议只考虑了一个候选人

template <class T> int compare(const T& v1, const T& v2);

为了成功,这个候选人应该为你的参数集通过模板参数推导。(模板参数推导过程也不关心任何额外的特化。)在这种情况下,模板参数推导失败,因为对于数组类型的参数模板参数T被推导为一个数组。你会得到两个论点的不相容推论。编译器为您提供了描述问题的错误消息。换句话说,在您的第一个示例中,模板的专用版本永远没有机会发挥作用。

在您的第二个示例中,您将专业化替换为重载,您为重载解决方案提供了第二个候选者。现在编译器同时看到了

template <class T> int compare(const T& v1, const T& v2);
int compare(const char* const& v1, const char* const& v2);

模板候选者像以前一样失败,而重载的候选者成功了。

为了更好地说明模板特化在这种情况下是如何工作的,我们可以获取您的原始代码并更改主模板,以便通过将参数彼此解耦来帮助它通过重载决议。如果在您的第​​一个示例中,您将模板声明更改为

template <class T1, class T2>
int compare(const T1& v1, const T2& v2) {
  ...

保持其他所有内容不变,代码将编译并使用您的专业化。但即使在这种情况下,带有推导参数的主模板也将被视为与您的参数更好的匹配(没有转换的立即引用绑定)。

于 2013-05-27T16:11:10.333 回答