为什么0 ==("abcde"+1)不是常量表达式?

emb*_*edc 20 c++ gcc language-lawyer constexpr c++14

为什么下面的代码没有编译?

// source.cpp

int main()
{
   constexpr bool result = (0 == ("abcde"+1));
}
Run Code Online (Sandbox Code Playgroud)

编译命令:

$ g++ -std=c++14 -c source.cpp
Run Code Online (Sandbox Code Playgroud)

输出:

source.cpp: In function ‘int main()’:
source.cpp:4:32: error: ‘((((const char*)"abcde") + 1u) == 0u)’ is not a constant expression
 constexpr bool result = (0 == ("abcde"+1));
                         ~~~^~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我正在使用gcc6.4.

Bar*_*rry 16

可以在常量表达式中使用的限制主要定义为否定列表.有一些你不被允许评估的东西(C++ 14中的[expr.const]/2)和值必须导致的某些东西(C++ 14中的[expr.const]/4).此列表从标准更改为标准,随着时间变得更加宽松.

在试图评估:

constexpr bool result = (0 == ("abcde"+1));
Run Code Online (Sandbox Code Playgroud)

没有什么我们不被允许评估,我们没有任何我们不允许的结果.没有未定义的行为等.这是一个完全有效的,如果奇怪的表达式.只是一个gcc 6.3碰巧不允许 - 这是一个编译器错误.gcc 7+,clang 3.5+,msvc都编译它.


围绕这个问题似乎存在很多混淆,许多评论表明,由于字符串文字的值"abcde"直到运行时才知道,所以在常量评估期间你不能对这样的指针做任何事情.解释为什么这不是真的很重要.

让我们从如下声明开始:

constexpr char const* p = "abcde";
Run Code Online (Sandbox Code Playgroud)

这个指针有一些价值.让我们说吧N.至关重要的是 - N 在不断的评估过程中,你可以做的任何事情都是不正确的.您不能将其强制转换为整数来读取值.你无法将它与不同的,无关的字符串(通过[expr.rel] /4.3)进行比较:

constexpr char const* q = "hello";
p > q; // ill-formed
p <= q; // ill-formed
p != q; // ok, false
Run Code Online (Sandbox Code Playgroud)

我们可以肯定地说,p != q因为无论它们指向何处,它们都明显不同.但我们不能说哪一个先行.这种比较是未定义的行为,并且在常量表达式中不允许使用未定义的行为.

你真的只能比较同一个数组中的指针:

constexpr char const* a = p + 1; // ok
constexpr char const* b = p + 17; // ill-formed
a > p; // ok, true
Run Code Online (Sandbox Code Playgroud)

无论p指向何处,我们都知道a之后的点.但我们不需要知道N确定这一点.

结果,N在持续评估期间的实际值或多或少是无关紧要的.

"abcde"是...在某个地方."abcde"+1指向一个晚于此,并具有价值"bcde".无论它指向何处,您都可以将其与空指针(0是空指针常量)进行比较,并且它不是空指针,因此该比较的计算结果为false.

这是一个非常完善的常量评估,gcc 6.3恰好拒绝了.


虽然我们只是通过命令std::less()(p, q)提供一些值,该值提供了一些在编译时提供一致的总顺序的值,并且它在运行时给出了相同的答案.这是......一个有趣的难题.