将字符数组从字符串字面值初始化,是否被视为隐式转换?

3
char x[10] = "banana";被认为是从const char[7]char[10]的隐式转换吗? 由于std::is_convertible<const char[7], char[10]>::value的值为false,显然答案是否定的。但是我找不到隐式转换的明确定义。阅读cppreference后,我认为原因是:

只要在不接受类型T1的上下文中使用该类型的表达式T1,而接受某些其他类型T2时,就会执行隐式转换;特别地:...当初始化类型为T2的新对象时,包括在返回T2的函数语句中;

虽然我不确定为什么他们没有将显式构造函数从这种情况中排除。
后续问题(可能没用): 数组是完全排除在任何类型转换之外(即数组转换为数组)吗?

1
数组完全不包括任何类型的转换吗?你真的没有听说过数组到指针的转换吗? - Language Lawyer
这是聚合初始化,可以在此处查看__字符数组__-https://en.cppreference.com/w/cpp/language/aggregate_initialization。没有发生任何转换。 - Richard Critten
char x[10] = {'b', 'a', 'n', 'a', 'n', 'a', '\0' };,其余三个字符会被隐式初始化为 '\0' - Eljay
@LanguageLawyer 第一个例子(字符数组)表明这两个初始化是等价的,cppreference 比我更懂 :) - Richard Critten
1
跟进 - 字符数组的声明和初始化在标准中有自己的章节dcl.init.string - Richard Critten
显示剩余3条评论
4个回答

4
在此声明中
char x[10] = "banana";

没有转换。字符串字面值的元素用于初始化数组的元素。实际上它等价于

char x[10] = { 'b', 'a', 'n', 'a', 'n', 'a', '\0' };

如果您声明了一个指针,而不是一个数组,就像这样:
const char *x = "banana";

那么,具有类型为 const char[7] 的字符串字面量将被隐式转换为其类型为 const char * 的第一个元素的指针。

以上声明等同于

const char *x = &"banana"[0];

2
为了更清晰,我认为你的第一句话应该以“没有转换”或者“既没有隐式转换也没有显式转换”结束。 - Tim Randall

4
用法律术语来说,从字符串字面量初始化字符数组是一种隐式转换。

[conv.general]:

如果某个表达式E的声明T t=E;对于某个虚构的临时变量t([dcl.init]),是良好形式的,那么该表达式E就可以被隐式转换为类型T。请注意,核心语言只定义了从表达式到类型的隐式转换。因此,“从const char [7]到char [10]的隐式转换”的含义是未定义的。当To是数组类型时,is_convertible::value为false,因为它被定义为在To不是有效返回类型(而数组不是)时产生false。(这可以用不同的方式实现。)

[meta.rel]/5:

The predicate condition for a template specialization is_­convertible<From, To> shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

To test() {
  return declval<From>();
}

数组很少被隐式转换为目标类型,因为它们既不是参数类型也不是返回类型。但是它们不被排除在临时材料化转换之外。


“从字符串字面量初始化字符数组是一种隐式转换” - 这感觉不太对。字符数组可以像这样写:char x[10] = {'b', 'a', 'n', 'a', 'n', 'a', '\0' }; 或者 char x[10] = "banana";,它们都会导致聚合(数组)初始化。我没有看到转换。 - Ted Lyngmo
@TedLyngmo 我说的是“律师语言”。实际上并不重要。 - cpplearner
我也是用“律师语言”的方式来表述 :-) 字符串字面值提供了一种初始化字符数组的额外方法。 - Ted Lyngmo
@TedLyngmo 标准以(复制)初始化的方式定义了隐式转换。因此,根据标准,即使是最奇怪的初始化也是一种隐式转换。虽然在实践中可能不直观或有意义。 - cpplearner
1
一个人永远不知道什么时候那些知识会派上用场。谢谢 :-) - Ted Lyngmo

2

我没有最新标准的副本,但在[dcl.init.string]中有以下段落:

如果初始化程序比数组元素少,则未显式初始化的每个元素都应进行零初始化(8.5)。
C++.2011§8.5.2¶3

因此,使用字面值初始化已知大小的char数组是指定的行为。


抱歉,但这个回答是否解决了我的问题?我找不到联系。 - domdrag
你的第一个问题,它的答案是否定的。 - jxh
数组不会通过赋值进行初始化,因此我不确定您对后续问题的答案想要什么。 - jxh
我有点糊涂。你的意思是这被认为是隐式转换还是不是? - domdrag
不是隐式转换,而是标准明确描述的初始化。 - jxh

2
为了进行重载决议,它被视为隐式转换,但不涉及任何实际转换(身份转换序列)。参见[over.ics.list]/4:
否则,如果参数类型是字符数组125且初始化列表具有适当类型的字符串字面值(9.4.2),则隐式转换序列是身份转换。 125由于没有数组类型的参数,因此这只会出现在引用参数的引用类型中。
对于数组类型本身(而不是对数组的引用),隐式转换似乎非常受限制:它显然只允许在变量定义中使用。类似于static_cast<char[10]>("banana")的东西无法编译(至少在GCC和Clang上)。

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