【问题标题】:Can a string literal be subscripted in a constant expression?字符串文字可以在常量表达式中下标吗?
【发布时间】:2026-01-20 11:15:01
【问题描述】:

这是有效的,因为constexpr 表达式允许采用“一个字面量类型的左值,它引用一个用 constexpr 定义的非易失性对象,或者引用这样一个对象的子对象"(§5.19/2):

constexpr char str[] = "hello, world";
constexpr char e = str[1];

但是,字符串文字似乎不符合此描述:

constexpr char e = "hello, world"[1]; // error: literal is not constexpr

2.14.5/8 描述了字符串字面量的类型:

普通字符串文字和 UTF-8 字符串文字也称为窄字符串文字。窄字符串文字的类型为“n const char 数组”,其中 n 是字符串的大小,定义如下,并且具有静态存储持续时间。

似乎这种类型的对象可以被索引,只要它是临时的而不是静态存储持续时间(5.19/2,就在上面的 sn-p 之后):

[constexpr 允许左值到右值转换] ... 一个字面量类型的左值,它引用一个非易失性临时对象,其生命周期尚未结束,用常量表达式初始化

这特别奇怪,因为获取临时对象的左值通常是“作弊”。我想这条规则适用于引用类型的函数参数,例如在

constexpr char get_1( char const (&str)[ 6 ] )
    { return str[ 1 ]; }

constexpr char i = get_1( { 'y', 'i', 'k', 'e', 's', '\0' } ); // OK
constexpr char e = get_1( "hello" ); // error: string literal not temporary

不管怎样,GCC 4.7 接受 get_1( "hello" ),但拒绝 "hello"[1],因为“'._0' 的值在常量表达式中不可用”......然而"hello"[1] 可接受作为案例标签或数组绑定。

我在这里分裂了一些标准的头发......分析是否正确,这个功能是否有一些设计意图?

编辑:哦……这有一些动机。似乎这种表达式是在预处理器中使用查找表的唯一方法。例如,这会引入一个代码块,除非SOME_INTEGER_FLAG 为 1 或 5,否则该代码块将被忽略,如果大于 6,则会导致诊断:

#if "\0\1\0\0\0\1"[ SOME_INTEGER_FLAG ]

这个结构对于 C++11 来说是新的。

【问题讨论】:

    标签: c++ c++11 string-literals constexpr


    【解决方案1】:

    这样做的目的是,说明左值到右值转换何时有效的段落将be amended with a note 指出引用字符串字面量子对象的左值是用常量表达式初始化的常量整数对象(被描述为允许的情况之一)在 C++11 后的草案中。

    您对预处理器中使用的评论看起来很有趣,但我不确定这是否有用。这是我第一次听说。

    【讨论】:

    • 我认为注释是不规范的,为什么要注释?
    • 我认为关键是措辞已经支持这种解释,只是不够明显,存在疑问;因此有一个注释可以帮助解释这个特殊的极端情况。不管怎样,这真是个好消息。
    • 有趣!由于 5.19 左值到右值转换下的案例 2 是指子对象,我不确定案例 1 是否适用于数组成员。因此,这些案例之间有很多重叠之处。至于预处理器技巧……#if 依靠将标识符压缩到0 来清理常量表达式。然后 16.1/4 应用,“结果标记包含控制常量表达式,根据 5.19 的规则使用至少具有 18.3 中指定范围的算术进行评估。”想一想,带有 UD 后缀的字符串不受 0 转换的影响!哦哦!
    • 是的,我同意。 ud-suffix 派生为标识符,但作为预处理标记(这是预处理器操作的最小单元),它是用户定义的字符串文字标记的一部分。所以根据我的理解,"foo"_x 永远不会转换为"foo"0。它保持"foo"_x,然后问题是后缀会发生什么:它被忽略了吗?这是一个错误吗?第三个(IMO 不可接受,因为它超出了预处理器)是 - 它会被查找吗?
    • 在我看来,根据现行法律条文,需要查一下。 UD-literal 具有由函数调用定义的值,并且 5.19 给出了关于允许哪些函数调用的规则。当然,如果它是模板的成员,这是不可能的。可能需要一份缺陷报告。第 16 条中的另一处假设字符串修饰仅由可能的前导 L 组成。而字符串只是一个特例;允许对尚未转换为数值的数字进行预处理。 GCC 和 Clang 拒绝 FP 值,即使没有标准禁止这些值。
    【解决方案2】:

    关于您关于#if 的问题,标准委员会无意增加可在预处理器中使用的表达式集,目前的措辞被认为是一个缺陷。这将在 Kona WG21 之后的邮件中列为核心问题 1436。感谢您提请我们注意!

    【讨论】:

    • DR 366 涵盖了第 5.19 节中的条件预处理问题。显然,该标准的工作草案已被修补,但该修复被后来的更改覆盖。新的修复可能属于 §16,因为当前的意图是拥有强大的常量表达式。
    最近更新 更多