为什么当使用字符串字面值初始化时,自动变量被推导为指针?

21
#include <iostream>
#include <typeinfo>

int main()
{
    const char a[] = "hello world";
    const char * p = "hello world";
    auto x = "hello world";

    if (typeid(x) == typeid(a))
        std::cout << "It's an array!\n";

    else if (typeid(x) == typeid(p))
        std::cout << "It's a pointer!\n";   // this is printed

    else
        std::cout << "It's Superman!\n";
}

当字符串字面量实际上是数组时,为什么x被推断为指针?

一个窄的字符串字面量的类型是"包含nconst char元素的数组" [2.14.5 字符串字面量 [lex.string] §8]


我的标准阅读对于auto和声明类型似乎表明它应该是一个数组,就像你期望的一样。它们清楚地表示它也像模板参数一样解析,这肯定会是一个数组(尽管我刚在GCC中测试了上述模板形式,它也报告为“指针”)。 - edA-qa mort-ora-y
3个回答

24

特性auto基于模板参数推导,而模板参数推导的行为是相同的,具体来说是根据§14.8.2.1/2 (C++11标准):

  • 如果P不是引用类型
    • 如果A是一个数组类型,则数组指针转换后生成的指针类型将用于类型推导

如果你想让表达式x的类型成为数组类型,只需在auto后面加上&

auto& x = "Hello world!";

那么,auto 占位符将被推导为 const char[13]。这与以引用作为参数的函数模板类似。只是为了避免任何混淆:x 的声明类型将是数组的 引用


1
请注意,这里的 x 不是一个数组,而是一个数组的引用。微妙的区别在于,在执行 auto& x = "a"; auto& y = "a"; 后,&x == &y 可能返回 true,这取决于两个不同的字符串字面量是否被合并。 - user743382
1
@hvd:没错,这就是为什么我特别说了“表达式x的类型”而不是“声明x的类型”。但是这当然值得一提。谢谢。 :) - sellibitze
3
auto&& x = "Hello world!"; 也是一种可能性(在这种情况下,它会产生一个指向数组的左值引用,就像 auto& 一样)。 - Luc Danton
2
@LucDanton 对,我总是忘记字符串字面量是左值。 - fredoverflow

6

当字符串字面量实际上是数组时,为什么x被推断为指针?

这是因为数组到指针的转换。

如果要将x推断为数组,则必须允许以下操作:

const char m[]          = "ABC";

const char n[sizeof(m)] = m; //error

在C++中,一个数组不能用另一个数组进行初始化(如上所示)。在这种情况下,源数组会退化为指针类型,您可以使用以下方法代替:

const char* n = m; //ok
< p > 使用 auto 进行类型推断的规则与函数模板中进行类型推导的规则相同: < /p >
template<typename T>
void f(T n);

f(m);     //T is deduced as const char*
f("ABC"); //T is deduced as const char*

auto n = m;     //n's type is inferred as const char*
auto n = "ABC"; //n's type is inferred as const char*

§7.1.6.4/6中提到了auto关键字:

然后,使用函数调用的模板参数推导规则(14.8.2.1)确定变量d的推导A,则变量d的类型被推导出来...


x 没有用 a 进行初始化。x"hello world" 进行了初始化,这是一个 char 数组的有效初始值。 - user743382
当然可以。你的回答只是解释了为什么auto x = a;会使x成为一个指针,但这并不是问题所在。 - user743382
除了一个差异:它们是字符数组的有效初始化器。您在自己的答案中有 const char m[] = "ABC"; ,正如您所说的,数组不是其他数组的有效初始化器! - user743382
1
您编辑后的版本似乎与GCC一致,即使您添加了引用限定符:template <typename T> void f(T&&); f("abc"); T被推断为数组。对于auto&& x = "abc";也是如此。 - user743382
@hvd:从规范中添加了引用。 - Nawaz

-2
如果您想让 x 被推断为数组,您可以使用:
decltype(auto) x = "hello world";

1
这里的 x 被推断为对数组的引用。 - HolyBlackCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接