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

Why is 0 == ("abcde"+1) not a constant expression?

为什么下面的代码不能编译?

// source.cpp

int main()
{
   constexpr bool result = (0 == ("abcde"+1));
}

编译命令:

$ g++ -std=c++14 -c source.cpp

输出:

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));
                         ~~~^~~~~~~~~~~~~~~

我正在使用 gcc6.4。

对常量表达式中可以使用的内容的限制主要定义为否定列表。有很多东西是不允许计算的(C++14 中的[expr.const]/2 in C++14) and certain things that values have to result in ([expr.const]/4)。此列表从一个标准变为另一个标准,随着时间的推移变得更加宽松。

在尝试评估:

constexpr bool result = (0 == ("abcde"+1));

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


关于这个问题似乎有很多困惑,许多评论表明,由于像 "abcde" 这样的字符串文字的值直到运行时才知道,所以你不能在运行期间对这样的指针做任何事情不断的评估。解释为什么这不是真的很重要。

让我们从这样的声明开始:

constexpr char const* p = "abcde";

这个指针有一些值。假设 N。关键是 - 在不断评估 期间,你试图观察 N 所做的任何事情都是病式的。你不能 cast it to an integer to read the value. You cannot compare it to a different, unrelated string (by way of [expr.rel]/4.3):

constexpr char const* q = "hello";
p > q; // ill-formed
p <= q; // ill-formed
p != q; // ok, false

我们可以肯定地说p != q,因为它们指向的地方明显不同。但我们不能说哪个先行。这样的比较是未定义的行为,常量表达式中的未定义行为is disallowed

你真的可以比较同一数组中的指针:

constexpr char const* a = p + 1; // ok
constexpr char const* b = p + 17; // ill-formed
a > p; // ok, true

无论 p 指向哪里,我们 知道 a 指向它后面。但是我们不需要知道 N 来确定这一点。

因此,持续评估期间的实际值 N 或多或少并不重要。

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

这是一个格式正确的常量求值,gcc 6.3 碰巧拒绝了它。


尽管我们简单地声明 std::less()(p, q) 提供了一些值,在编译时给出了一致的总顺序 and 它在运行时给出了相同的答案。这是……一个有趣的难题。