【问题标题】:Passing string literals by reference to const char* fails to compile with g++ 4.6.3通过引用 const char* 传递字符串文字无法使用 g++ 4.6.3 编译
【发布时间】:2013-05-22 12:04:32
【问题描述】:

这是 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 不会编译这段代码并给出如下错误:

错误:没有匹配函数调用 'compare(const char [4], const 字符 [5])'

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

现在给出以下事实:

我。字符串字面量,或 C++ 中的 C 风格字符串实际上是一个 const 字符数组.

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

这里我只是将字符串文字“abc”和“defg”作为reference 传递给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&amp; 传递字符串文字?

【问题讨论】:

  • 您的第二个比较重载看起来像您的第一个 - 都采用 const char * - 编辑?
  • "然后再次编译失败" - 对我来说适用于 clang 和 g++。
  • "abc","defg" 不是const char*
  • @很快我再次尝试了“然后编译再次失败”并且它工作......问题已编辑

标签: c++ templates pointers


【解决方案1】:

下面的答案是基于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" 的类型为 char const[4],而 "defg" 的类型为 char const[5]。它们是不同的类型。由于专用版本和模板版本都需要引用参数,因此在参数推导期间没有数组到指针的衰减。因此,您不能将不同长度的字符串文字传递给它们以找到匹配项。如果你提供一个不需要任何参数推导的常规函数​​,编译器会找到一个匹配项。

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

【讨论】:

  • 感谢您的回答。只需添加我的 2 美分,strcmp 即可返回差额。如果我们想与示例匹配,我们应该这样做:return compare(strcmp(a, b), 0);
【解决方案2】:

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

模板特化只在以后并且只有当它们的主要模板“赢得”重载决议时才会发挥作用。 IE。模板特化在特化的过程中使用(顾名思义),它们在重载解析期间是完全不可见的。

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

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) {
  ...

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

【讨论】:

    猜你喜欢
    • 2018-09-01
    • 2013-04-07
    • 2017-02-06
    • 2020-01-03
    • 2014-08-11
    • 2017-07-25
    • 2016-07-05
    • 1970-01-01
    • 2014-02-03
    相关资源
    最近更新 更多