为什么constexpr似乎不意味着char*的const?

71
constexpr显然意味着const,因此常常会看到这样的写法:
constexpr int foo = 42; // no const here

然而,如果你写的是:
constexpr char *const str = "foo";

然后如果传递了-Wwrite-string标志,GCC将生成“警告:从字符串常量转换为'char *'已弃用”的警告。
写作:
constexpr const char *const str = "foo";

解决了问题。
所以,constexpr const和constexpr真的是一样的吗?

11
这不是 Difference between constexpr and const 的重复,这里的问题是关于“constexpr const”和“constexpr”两者的组合使用,与所链接的问题完全不同。 - Anubis
1
@Sumudu,我完全同意,并投票重新开放问题。 - NicholasM
3个回答

112

问题在于,对于变量声明而言,constexpr 总是将 const 应用于声明的对象;而另一方面,const 可以根据其放置的位置应用于不同的类型。

因此,

constexpr const int i = 3;
constexpr int i = 3;

等价的;

constexpr char* p = nullptr;
constexpr char* const p = nullptr;

它们是等效的;两者都将p变为指向char类型的const指针。

constexpr const char* p = nullptr;
constexpr const char* const p = nullptr;

等价。 constexpr 使 p 成为一个 const 指针。 const char * 中的 const 使得 p 指向 const char


constexpr const char* p = nullptr;constexpr char* p = nullptr; 是等价的吗? - IC_
4
不,它们并不相同。constexpr const char* p = nullptr; 声明了 p 是一个指向常量字符数据的常量指针,因此既不能修改 p 也不能修改它所指向的数据。constexpr char* p = nullptr; 声明了 p 是一个指向字符数据的常量指针,因此不能修改 p,但是可以修改 p 所指向的数据。 - qbert220
1
我认为@qbert220所写的内容是必要的,应该添加到答案中。 - Paul

7
你看到的错误信息与 constexpr 关键字本身无关。
例如 "foo" 这样的字符串字面量:
somefunction("foo");

这个字符串字面值的类型是const char *。以下声明:
char *const str = "foo";

这里试图将一个const char *值赋给一个char *值。结果得到的char *值是不可变、常量的,但此时错误已经发生:试图将const char *转换为char *

你示例中的constexpr关键字只是一个干扰,与错误无关。


虽然如此,我认为OP是正确的,这应该对错误有影响。constexprconstexpr const应该是相同的东西。 - Mooing Duck
1
问题在于将const char *转换为char *。无论结果值是const还是constexpr,都不是一个因素。错误已经发生在那一点上。只使用普通的char *foo="bar"也会出现相同的错误。 - Sam Varshavchik
3
"foo"的类型是const char [4] - T.C.
你似乎在将 constexpr 附加到指针上,但我认为它应该附加到字符上。我完全知道字符串字面值不能转换为 char*,但是每个 char 本身都应该是 constexpr,因此字面值“应该”(逻辑上)可以分配给 constexpr char* - Mooing Duck
5
不。constexpr 是附加在正在声明的对象上的。正在声明的对象不是指针所指向的单个字符,而是指针本身。 - Sam Varshavchik

-2

不。说它们是相同的意味着,在不使用const而不生成与const版本功能完全相同的代码的任何时间都是无效的。

我发现这在创建安全单例模式中很有用。我还没有完全探索此功能,但预计非const constexpr还有其他有效用途。

以下是需要非const constexpr的代码示例:

从全局定义变量开始:

int global_int_;

现在我们可以创建一个返回其引用的constexpr函数:
constexpr int& get_global()
{
    return global_int_;
}

现在我们可以在其他地方使用该引用:

int main()
{
    constexpr int& i{ get_global() };
    // do stuff with i
    return 0;
}

现在我们可以将 i 用作非 const int。如果隐含了 const,这是不可能的。

由于非 const constexpr 是有效的,如果您正在使用需要为 const 的 constexpr,则需要显式声明它。


3
这完全是个转移话题。C++14中的constexpr-not-implicitly-const是用于成员函数的。 - T.C.
@T.C. 我提供了一个非 const constexpr 的例子,没有成员函数,所以我不确定你的意思是什么。 - Michael Gazonda
1
那与 C++14 的变化无关。就像指针一样,constexpr 应用于引用本身(因为引用本身不能被改变),而不是所引用的对象。 - T.C.
@T.C. 你说的C++11/14是对的,我已经修复了。我的观点是,在上面的代码中添加const会删除有效的功能,这就是为什么不能暗示const的原因。 - Michael Gazonda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接