能否使用有条件选择的字符串字面量来初始化字符数组?

15

我知道使用字符串字面值可以完全初始化一个char数组:

char arr[] = "foo";

C++11 8.5.2/1规定:

一个字符数组(无论是普通的charsigned char还是unsigned char)、char16_t数组、char32_t数组或wchar_t数组可以通过窄字符字面值、char16_t字符串字面值、char32_t字符串字面值或宽字符串字面值,或用括号括起来的适当类型的字符串字面值进行初始化。字符串字面值的值的连续字符初始化数组的元素。...

然而,在条件表达式中,能否使用两个字符串字面值做同样的事情呢?例如像这样:

char arr[] = MY_BOOLEAN_MACRO() ? "foo" : "bar";

(其中MY_BOOLEAN_MACRO()将扩展为10。)

C++11 5.16(条件运算符)的相关部分如下:

1…第一个表达式被上下文转换为bool(第4章)。它被评估,如果它是true,条件表达式的结果是第二个表达式的值,否则是第三个表达式的值。...

4 如果第二个和第三个操作数是相同值类型的glvalue,并且具有相同的类型,则结果是该类型和值类别,如果第二个或第三个操作数是位字段,或两者都是位字段,则是位字段。

请注意,这些字面量的长度相同,因此它们都是const char[4]类型的lvalue。

GCC one ideone接受这种构造方式。但从标准的阅读中,我并不确定它是否合法。有没有人有更好的见解?

2个回答

10

另一方面,clang 不接受这样的代码(可以在此查看),我认为 clang 在这方面是正确的(MSVC 也拒绝此代码)。

一个 字符串字面量 在第 2.14.5 节的语法中被定义:

string-literal:
    encoding-prefixopt" s-char-sequenceopt"
    encoding-prefixoptR raw-string

这一部分的第一个段落如下所示 (强调是我的):

字符串字面值是由双引号包围的字符序列(在2.14.3中定义), 可以前缀为R、u8、u8R、u、uR、U、UR、L或LR,例如"...","(...)", u8"...", u8R"(...)",u"...", uR"˜(...)˜", U"...", UR"zzz(...)zzz", L"..."或LR"(...)"。

它进一步说明了窄字符串字面值的类型:

"n个const char的数组"

及:

具有静态存储期。

但是,带有静态存储期的"n个const char的数组"不是字符串文字,因为它不符合语法,也不符合第1段的规定。

如果我们使用非常量表达式,则可以使其在gcc上失败(查看实际情况):

bool x = true ;
char arr[] = x ? "foo" : "bar";

这意味着它很可能是一个扩展,但不符合标准,因为在strict conformance mode即使用-std=c++11 -pedantic时没有产生警告。参考第1.4[intro.compliance]

[...]实现必须诊断使用此国际标准中不符合规范的扩展的程序。然而,在这样做之后,他们可以编译和执行这些程序。


3
同意,8.5.2/1 看起来非常清楚,它是 字符串字面量 而不是 某个表达式求值得到的字符串字面量。(即使在这种情况下考虑三元运算符也会被解释为求值得到一个字符串字面量)。 - M.M

3

这在C++11或更新的版本中适用于GCC,因为您提供的字面量在编译时是确定性的(例如,它们是constexpr)。由于编译器可以找出哪一个是真实的,它可以决定使用哪一个。

如果要删除constexpr能力,请尝试类似于以下内容:

#include <iostream>
#include <cstdlib>

int main() {
    bool _bool = rand();
    char arr[] = (_bool) ? "asdf" : "ffff";

    std::cout << arr << std::endl;
}

GCC随后报错:
g++ test.cpp -std=c++11
test.cpp: In function ‘int main()’:
test.cpp:6:34: error: initializer fails to determine size of ‘arr’
  char arr[] = (_bool) ? "asdf" : "ffff";
                                  ^
test.cpp:6:34: error: array must be initialized with a brace-enclosed initializer

我不太了解标准的文本定义,不知道这个是否有效,但我感觉它是有效的。

如果想进一步了解constexpr及其对编译性的影响,请参阅另一个问题中@ShafikYaghmour的答案


1
请注意,常量表达式constexpr是相关但不相同的,我在这里部分涵盖了这一点(https://dev59.com/DV8e5IYBdhLWcg3wJn2l#26025026),因此`gcc`将其视为常量表达式。 - Shafik Yaghmour
没错,我能看出这有所区别。但这只是一个微小的点,我认为在C++11及以后的版本中并不适用...除非你能证明它确实适用? - inetknght
1
这只是纯粹的信息,希望对你有所帮助。我想指出的是,由于问题标记为language-lawyer,人们会更加挑剔细节。 - Shafik Yaghmour

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