为什么必须将 const 添加到 constexpr 以进行字符串文字声明?

fir*_*ush 22 c++ constants constexpr

本声明:

char constexpr *const s = "hello";
Run Code Online (Sandbox Code Playgroud)

失败并出现此错误:

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";
Run Code Online (Sandbox Code Playgroud)

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

char const constexpr *const s = "hello";
Run Code Online (Sandbox Code Playgroud)

汇编:

g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
hello
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎不直观。为什么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.
Run Code Online (Sandbox Code Playgroud)

在这里,char * const s意味着指针 s 是常量,而它取消引用的字符是可修改的。另一方面:

char const * s = "hello";
a[0] = 'H'; // Compiler error
s = "there"; // OK
Run Code Online (Sandbox Code Playgroud)

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

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

char constexpr * const s = "hello";
Run Code Online (Sandbox Code Playgroud)

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

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

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

char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";
Run Code Online (Sandbox Code Playgroud)

然而,这有效:

constexpr char const * s = "hello";
Run Code Online (Sandbox Code Playgroud)

上面有我们想要的,这意味着:

  • 字符是 const 通过 const
  • 并且指针s是 const 和编译时常量通过constexpr

Sto*_*ica 12

constexpr 不意味着 const 吗?

它确实,在被声明的对象上,在你的情况下s。申请的结果constexpr是对象

char *const s;
Run Code Online (Sandbox Code Playgroud)

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

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

不。但话又说回来,constexpr这里不允许更改声明的对象。例如

static char foo[] = "abc"; // Not a constant array
constexpr  char * s  = foo; // But the address is still a valid initializer.
Run Code Online (Sandbox Code Playgroud)

是一对有效的声明。