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

Why does const have to be added to constexpr for a string literal declaration?

本文关键字:字符串 文字 声明 const 添加 constexpr 为什么      更新时间:2023-10-16

此声明:

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

这对我来说似乎不直观。为什么康斯特需要装饰康斯特普?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 是常量,而它取消引用的字符是可修改的。另一方面:

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

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

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

char constexpr * const s = "hello";

我认为这意味着指针是常量(确实如此(,而字符本身将是常量和常量。但是语法不是这样工作的。相反,在这种情况下,constexpr:

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

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

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

但是,这有效:

constexpr char const * s = "hello";

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

  • 字符通过const
  • 指针s是常量,并且通过constexpr

constexpr 不是意味着 const 吗?

在所声明的对象上,在您的情况下,它确实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 *const s = "hello";中,const应用于*,而不是char,所以s是一个指向非常量char数据的常量指针。 但是,字符串文本是常量字符数据(在本例中,"hello"是一个const char[6](。 您不能有一个指向实际指向常量数据的非常量数据的指针,这将允许常量数据可修改,如果某些东西实际尝试修改数据,这是未定义的行为。 这就是编译器错误所抱怨的。

因此,您需要一个指向常量字符数据的指针:

char const *const s = "hello";

或:

const char *const s = "hello";

constexpr只是使s变量在编译时可用于计算。

感谢StoryTeller的回答和Firebush的解释,这教会了我很多。

而把我带到这里的问题是关于arrayconst,我做了一些简单的测试,比如Firebush。

关于数组,const总是阻止修改特定维度而不是我认为可能对某些内容有所帮助的所有内容,所以我在这里发布测试代码和评论。

char* const strs1[] = {"uu", "vv"}; // protected 1st dimension
const char* strs2[] = {"ww", "xx"}; // protected 2nd dimension
char* strs3[] = {"yy", "zz"};
strs1[0] = "aa"; // error, try to modify 1st dimension
strs1[0][0] = 'a';
strs2[0] = "aa";
strs2[0][0] = 'a'; // error, try to modify 2nd dimension
strs1 = strs3; // error, try to modify 1st dimension
strs2 = strs3; // error, try to modify 2nd dimension

真相都在上面,最大的遗憾就是错过了几句话的简短有效的总结,让我们大家永远不要忘记const的用法。

static constexpr auto NONE = "none";