【问题标题】:C++ Template issue with visual studio 2012Visual Studio 2012 的 C++ 模板问题
【发布时间】:2013-06-26 14:11:28
【问题描述】:

我目前正在阅读一本关于模板的书,它给出了以下示例:

#include <string>

// note: reference parameters
template <typename T>
inline T const& max (T const& a, T const& b)
{
    return  (a < b)  ?  (b) : (a);
}

int main()
{
    char* a="apple";
    char* p="peach"; 
    ::max(a,p);   // OK, BUT 
    ::max("apple","peach");//  <<<< ERROR IN VISUAL STUDIO 2012, WHY?
}

我不明白为什么VS2012说:

错误 C2440: 'return' : 无法从 'const char *' 转换为 'const char (&)[6]'

请有人向我解释一下吗? 书上说应该没问题,不过是旧书,还在等新书。

【问题讨论】:

  • 看起来T const&amp; aconst T&amp; a 是不同的东西......
  • @AlexFarber 不,他们不是。他在整个代码中使用T const&amp;
  • 如果不使用,为什么还要包含string
  • 即使你得到它的工作(下面的答案),它只会比较指针值,字符串内容!
  • 这是一个理论问题,而不是基于现实的问题。我试图了解使模板编程如此反直觉的微妙之处。包含字符串是因为样本较大,在某些时候模板 max 也与 2 std::string 一起使用,但我认为这与所提出的问题无关。

标签: c++ templates visual-studio-2012


【解决方案1】:

问题在于,在第二种情况下,您的 T const&amp; 被推导出为 const char(&amp;)[6],这是对 6 个字符数组的引用。

在 VS2012 中,当您使用 ?: 进行比较时,它会衰减对 char* 的数组引用,然后无法将其作为引用返回。 GCC 4.7.2 没有表现出这种衰减(参见https://ideone.com/yIBZWi)。

你能试试下面不那么紧凑的版本吗(我手头没有 VS2012)?在 return 中不同时使用 a 和 b 应该可以避免衰减。

template <typename T>
inline T const& max (T const& a, T const& b)
{
    if(a<b)
       return b;
    else
       return a;
}

【讨论】:

  • 另外,它只会比较指针值,而不是字符串内容。
  • 这编译正常(不给出错误),所以 ?: 绝对是原因。谢谢你的洞察力。显然,该代码不能按 C 字符串的预期工作(这首先是书中的重点)。如果你想让模板功能正确,你显然需要专门化它
  • @Pifcnt:不要专门化,重载,尤其是在这种情况下,为了摆脱不同长度的问题——对于重载,编译器会尝试应用转换:ideone.com/iWS8YL
【解决方案2】:

错误原因如下:

字符串文字的类型为const char[X],其中X 是字符串的长度,包括零分隔符。在您的情况下,它是 6,所以总共 Tconst char[6]
无法将 C 数组与关系运算符进行比较,因此编译器将“数组应用于指针衰减”,这意味着在 (a &lt; b) 中,a 和 b 被视为指针。
似乎 Visual Studio 将这种衰减应用于整个表达式,即它也将 a 和 b 视为 (b) : (a) 中的指针。
整个表达式的类型为char const*,但返回类型为T const&amp;,即const char(&amp;)[6],并且指针不能向后转换。我认为这是 VS2012 中的一个错误。

更新: 这是一个标准引用来支持这是一个错误的说法:
§5.16,4:

如果第二个和第三个操作数是相同值类别的glvalues并且具有相同的类型,则结果是该类型和值类别[...]

旁注 1:您包括 &lt;string&gt;,这是不可能的。请注意,字符串文字不是 std::strings,大多数是出于与 C 向后兼容的原因。
旁注 2:您将字符串文字分配给 char *,这已被弃用。字符串字面量是char const[] 类型,因此将它们衰减为char* 是一种特殊性,同样是由于向后兼容的原因。如果您确实将该指针后面的字符串视为非常量并写入它们,您将获得未定义的行为。所以更喜欢char const*,你的编译器无论如何都会警告你。
旁注 3:在 C++ 中,指针不是完全有序的。因此,衰减数组的比较,max 函数中指针的含义是实现定义的,您将希望在可移植程序中避免这种情况。无论如何,它没有任何意义,因为它是地址,而不是字符串文字内容。 旁注 4:你很幸运,“apple”和“peach”的长度相同。如果他们不是,例如“apples”和“peaches”,编译器会看到 const char[7]const char[8] 类型的字符文字,并且不知道 T 应该是什么:http://ideone.com/fp2WmJ

【讨论】:

  • 我特别喜欢sidenote 2,我一直有声明char * =“”不一致的问题,但它再次被广泛使用,所以在罗马时...... 关于Sidenote4:我做了不走运,这本书特别使用了 2 个完全相同大小的字符串来强调你刚才所说的内容:)(也让我觉得自己很愚蠢大约 10 分钟)。这就是我总是说模板应该只在绝对必要时使用的原因之一,而不是像我遇到的大多数程序员所认为的那样自动使代码通用且易于维护的灵丹妙药。
【解决方案3】:

在实例化函数模板max时,模板参数T是从实参推导出来的。在您的示例中,它的类型为 char const [6],一个由六个字符组成的数组。

表达式(a &lt; b) ? (b) : (a) 会将(a)(b) 转换为指针(规则5.16.6,“数组到指针(4.2),[...] 标准转换在第二个和第三个操作数”。)

然后编译器会尝试将生成的char const * 转换为T(返回类型),即char const[6],因此会出现错误。

GCC 不会表现出这种行为,因为它不会将数组转换为指针如果它们是相同的类型,即相同大小的 char 数组。只要两个 char 数组的大小不同,错误仍然会出现。

无论如何,比较 char 数组可能不会像你想的那样工作,所以最好只使用 std::string 代替。

【讨论】:

  • §5.16,4 在 §5.16,6 之前,并说如果两者“都是相同值类别的 glvalues 并且具有相同类型,则结果是该类型”,所以它应该 not 衰减到char const* 不同大小的数组将无法编译,因为编译器无法推断出T
  • @ArneMertz 你是对的,看来这个错误是在 MSVC 方面。
  • @not-kbok 我很清楚 '
猜你喜欢
  • 2013-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多