这个声明:
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";
但如果我在constexpr前面加上const,编译器就会很高兴:
char const constexpr *const s = "hello";
编译:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
hello
对我来说,这似乎不直观。为什么const需要修饰constexpr?constexpr不意味着const吗?如果它是一个编译器常量,在某种意义上它不是一个常量吗?某物可能是constexpr但以某种其他方式发生变化,使它不再是常量吗?
这是一个极简的godbolt链接:
更新:
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,而不是指针本身。好的,大多数使用const和指针的人都理解这一点。我被搞糊涂的是我认为constexpr也会按照这种方式工作。也就是说,考虑到这个:
char constexpr * const s = "hello";
我原以为这将意味着指针是const(它确实是),而字符本身将是const和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
通过constexpr
修饰为编译时常量且为常量指针