【问题标题】:Why does const have to be added to constexpr for a string literal declaration?为什么必须将 const 添加到 constexpr 才能进行字符串文字声明?
【发布时间】:2020-08-28 15:47:12
【问题描述】:

此声明:

char constexpr *const s = "hello";

失败并出现此错误:

g++ -g -Wall -Werror -std=c++17 test.cc -o test
test.cc:8:31: error: ISO C++11 does not allow conversion from string literal to 'char *const' [-Werror,-Wwritable-strings]
char constexpr *const s = "hello";

但是如果我将 const 添加到 constexpr,编译器会很高兴:

char const constexpr *const s = "hello";

编译:

g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
hello

这对我来说似乎不直观。为什么const需要修饰constexpr? constexpr 不意味着 const 吗?如果它是编译器常量,那么它在其他意义上怎么不是常量?是否有可能某些东西是 constexpr 但以其他方式改变而不是恒定的?

这是一个最小的神螺栓:

https://godbolt.org/z/sSQMVa


更新:

StoryTeller 的回答是理解这一点的关键。我已经接受了他的回答,但我会在这里扩展它,以防其他人试图理解这一点。与 const 交互时,我习惯于将 const 视为应用于其左侧的项目。因此:

char a[] = "hello";
char * const s = a;
s[0] = 'H'; // OK
s = "there"; // Compiler error.

这里,char * const s 表示指针 s 是 const 而它取消​​引用的字符是可修改的。另一方面:

char const * s = "hello";
a[0] = 'H'; // Compiler error
s = "there"; // OK

在这种情况下,char const * s 表示 s 指向的字符是 const,而不是指针。

好的,大多数使用过 const 和指针的人都明白这一切。我被抛弃的地方是我认为 constexpr 也会以这种方式工作。也就是说,鉴于此:

char constexpr * const s = "hello";

我认为这意味着指针是 const(它是)并且字符本身是 const 和 constexpr。但是语法不是那样工作的。相反,在这种情况下的 constexpr:

  • 不适用于角色,而是...
  • 适用于s本身,它是一个指针,并且...
  • 因此与指针后面的 const 是多余的。

因此,在这种情况下,没有在字符上声明 const。事实上,如果我完全删除 constexpr,我会得到完全相同的错误:

char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";

但是,这有效:

constexpr char const * s = "hello";

上面有我们想要的,意思是:

  • 字符是常量,通过const
  • 而指针s 是const 和一个编译时间常数通过constexpr

【问题讨论】:

  • 很好的解释!谢谢。

标签: c++ constants constexpr


【解决方案1】:

constexpr 不暗示 const 吗?

确实如此,在声明的 object 上,在您的情况下为 s。应用constexpr的结果是对象

char *const s;

它仍然被声明指向一个非常量对象。只有地址必须是常量表达式。这意味着它必须是具有静态存储持续时间的对象。

是否有可能某些东西是 constexpr 但以其他方式改变而不是恒定的?

没有。但话又说回来,此处允许更改的不是被声明为constexpr 的对象。比如

static char foo[] = "abc"; // Not a constant array
constexpr  char * s  = foo; // But the address is still a valid initializer.

是一对有效的声明。

【讨论】:

  • 呃。我习惯于 const 应用于左边的东西。因此,char constexpr * const s = "hello"; 我的意思是传达 char 数组是 constexpr,而不是指针。但是根据您向我指出的内容, constexpr 不是那样工作的,而是适用于指针。好的,这增加了警告的清晰度。确实,这与来自char * const s = "hello"; 的警告相同
【解决方案2】:

const 适用于它左边的东西,或者如果什么都没有,则适用于它的右边。

char *const s = "hello"; 中,const 应用于*,而不是char,因此s 是一个指向非常量char 数据的常量指针。但是,字符串文字 是 const char 数据(在这种情况下,"hello"const char[6])。您不能拥有指向实际指向 const 数据的非常量数据的指针,这将允许修改 const 数据,如果某些东西实际上试图修改数据,这是未定义的行为。这就是编译器错误所抱怨的。

因此,您需要一个指向 const char 数据的指针:

char const *const s = "hello";

或者:

const char *const s = "hello";

constexpr 只是使s 变量可用于在编译时进行评估。

【讨论】:

  • @bolov - 我想他的意思是“constexpr 不会改变类型”,因为在这种情况下,类型已经是 const 类型。但是,是的......也许可以以错误的方式使用。
  • 为了避免人们对此感到困惑,我必须恭敬地指出你的最后一句话是不正确的。我也对此感到困惑。 constexpr 确实暗示了 const 并且会改变类型,但它令人困惑地适用于指针 s 而不是它指向的字符,即使放在 char 之后也是如此。因此constexpr char const * s = "hello"; 将 s 的类型从可修改指针更改为 const 指针。也就是说,它暗示了指针后面的const。此外,它等价于 char const constexpr * s = "hello"; :即使在 char 之后应用,它也适用于指针。
【解决方案3】:
static constexpr auto NONE = "none";

【讨论】:

  • A code-only answer is not high quality。虽然此代码可能很有用,但您可以通过说明其工作原理、工作方式、何时应该使用以及它的局限性来改进它。请edit您的回答包括解释和相关文档的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多