为什么必须将 const 添加到 constexpr 以进行字符串文字声明?
Why does const have to be added to constexpr for a string literal declaration?
此声明:
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 但以其他方式改变而不是恒定的?
这是一个最小的神螺栓:
更新:
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。但是语法不是那样工作的。相反,本例中的 constexpr:
- 不适用于角色,而是...
- 适用于
s
本身,它是一个指针,并且...
- 因此指针后面的const是多余的
因此,在这种情况下,没有对字符声明 const。事实上,如果我完全删除 constexpr,我会得到完全相同的错误:
char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";
然而,这有效:
constexpr char const * s = "hello";
上面有我们想要的,意思是:
- 字符是 const via
const
- 指针
s
是常量,通过constexpr
编译时间常量
Doesn't constexpr imply const?
在 对象 上,在您的情况下 s
确实如此。应用 constexpr
的结果是对象
char *const s;
它仍然被声明为指向一个非常量对象。只有地址必须是常量表达式。这意味着它必须是一个具有静态存储持续时间的对象。
Is it possible for something to be constexpr but change in some other way such that is not constant?
没有。但话又说回来,它不是被声明的对象 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
是指向非const char
数据。但是,字符串文字 是 const char 数据(在这种情况下,"hello"
是 const char[6]
)。您不能拥有指向实际上指向 const 数据的非常量数据的指针,这将允许 const 数据是可修改的,如果某些东西实际上试图修改数据,这是未定义的行为。这就是编译器错误所抱怨的。
因此,您需要一个指向 const char 数据的指针:
char const *const s = "hello";
或:
const char *const s = "hello";
constexpr
只是使 s
变量在编译时可用于计算。
static constexpr auto NONE = "none";
感谢 StoryTeller 的回答和 Firebush 的解释,让我受益匪浅。
这里的问题是关于 const
和 array
,我做了一些简单的测试,比如 Firebush。
关于数组,const
总是防止修改特定维度而不是所有内容,我认为这可能对某些人有帮助,所以我 post 在这里测试代码和注释。
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
的用法。
此声明:
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 但以其他方式改变而不是恒定的?
这是一个最小的神螺栓:
更新:
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。但是语法不是那样工作的。相反,本例中的 constexpr:
- 不适用于角色,而是...
- 适用于
s
本身,它是一个指针,并且... - 因此指针后面的const是多余的
因此,在这种情况下,没有对字符声明 const。事实上,如果我完全删除 constexpr,我会得到完全相同的错误:
char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";
然而,这有效:
constexpr char const * s = "hello";
上面有我们想要的,意思是:
- 字符是 const via
const
- 指针
s
是常量,通过constexpr
编译时间常量
Doesn't constexpr imply const?
在 对象 上,在您的情况下 s
确实如此。应用 constexpr
的结果是对象
char *const s;
它仍然被声明为指向一个非常量对象。只有地址必须是常量表达式。这意味着它必须是一个具有静态存储持续时间的对象。
Is it possible for something to be constexpr but change in some other way such that is not constant?
没有。但话又说回来,它不是被声明的对象 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
是指向非const char
数据。但是,字符串文字 是 const char 数据(在这种情况下,"hello"
是 const char[6]
)。您不能拥有指向实际上指向 const 数据的非常量数据的指针,这将允许 const 数据是可修改的,如果某些东西实际上试图修改数据,这是未定义的行为。这就是编译器错误所抱怨的。
因此,您需要一个指向 const char 数据的指针:
char const *const s = "hello";
或:
const char *const s = "hello";
constexpr
只是使 s
变量在编译时可用于计算。
static constexpr auto NONE = "none";
感谢 StoryTeller 的回答和 Firebush 的解释,让我受益匪浅。
这里的问题是关于 const
和 array
,我做了一些简单的测试,比如 Firebush。
关于数组,const
总是防止修改特定维度而不是所有内容,我认为这可能对某些人有帮助,所以我 post 在这里测试代码和注释。
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
的用法。