21

Why doesn't the following code compile?

// source.cpp

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

The compile command:

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

The output:

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

I'm using gcc6.4.

Passer By
  • 19,325
  • 6
  • 49
  • 96
embedc
  • 1,485
  • 1
  • 6
  • 20
  • you just want to know why or is there some real use case? I mean you could write `constexpr bool result = false;` to get the same result – 463035818_is_not_an_ai Jan 29 '19 at 15:13
  • I want to know why because this code is extracted from a real code. – embedc Jan 29 '19 at 15:14
  • 1
    oh really? now I am puzzled... what is this supposed to do? Afaik you can never get `0` from adding something to a pointer – 463035818_is_not_an_ai Jan 29 '19 at 15:15
  • 3
    Maybe related: https://stackoverflow.com/questions/7392057/why-cant-a-constant-pointer-be-a-constant-expression – Sergey Jan 29 '19 at 15:16
  • 8
    GCC 7.x and above compile your code without any errors (with `-std=c++14 -pedantic-errors` flags). Clang and MSVC also don't complain. A GCC 6 bug maybe? – HolyBlackCat Jan 29 '19 at 15:19
  • @user463035818 It doesn't matter whether you compare to 0 or another constexpr string. The code seems to be OK - why doesn't it compile? – embedc Jan 29 '19 at 15:19
  • 1
    MSVS 2017 compiles your code successfully. – Sergey Jan 29 '19 at 15:20
  • The current version of GCC is 8.2. Does GCC 8.2 trip this error? – Eljay Jan 29 '19 at 15:26
  • erm yes it does matter, `0` is not a constexpr string, it is an integer literal that you compare to the value of a pointer. There is only one pointer with the value `0` ;) and a pointer can never be `0` after adding something to it – 463035818_is_not_an_ai Jan 29 '19 at 15:28
  • 5
    @user463035818 Of course the expression is `false`, OP doesn't argue with that. The question is why it doesn't compile. – HolyBlackCat Jan 29 '19 at 15:32
  • 1
    @HolyBlackCat yeah, there was a small misunderstanding, in the mean time i got it...(still if this was extracted from real code, I would "fix" that code) – 463035818_is_not_an_ai Jan 29 '19 at 15:37
  • for grins what if you put spaces around the plus/add? – old_timer Jan 29 '19 at 15:45
  • 2
    https://godbolt.org/z/oYNZIQ reproduces the error. If you replace `"abcd"` with `&"abcd"[0]`, it works (even inline). It seems that the `const char[]` -> `const char*` decay isn't counted as `constexpr` for whatever reason. – Artyer Jan 29 '19 at 15:53
  • I'm voting to close this question as off-topic because the program has been demonstrated to work on numerous different compilers in the comments, this is just a compiler bug. – Barry Jan 29 '19 at 19:42
  • 1
    @Barry It's only a compiler bug if the standard requires it to work, which nobody has yet argued as far as I can see. – David Schwartz Jan 29 '19 at 22:54
  • @DavidSchwartz Alright, I can write an answer I guess - but it's not really going to be interesting. – Barry Jan 29 '19 at 23:10
  • The value of `"abcde"` is not known at compile time. It's as simple as that. It isn't known until after link time. – user207421 Jan 29 '19 at 23:14
  • @DavidSchwartz Alright, maybe it'll end up being more interesting than I anticipated? – Barry Jan 30 '19 at 04:49

1 Answers1

17

The restrictions on what can be used in a constant expression are defined mostly as a list of negatives. There's a bunch of things you're not allowed to evaluate ([expr.const]/2 in C++14) and certain things that values have to result in ([expr.const]/4 in C++14). This list changes from standard to standard, becoming more permissive with time.

In trying to evaluate:

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

there is nothing that we're not allowed to evaluate, and we don't have any results that we're not allowed to have. No undefined behavior, etc. It's a perfectly valid, if odd, expression. Just one that gcc 6.3 happens to disallow - which is a compiler bug. gcc 7+, clang 3.5+, msvc all compile it.


There seems to be a lot of confusion around this question, with many comments suggesting that since the value of a string literal like "abcde" is not known until runtime, you cannot do anything with such a pointer during constant evaluation. It's important to explain why this is not true.

Let's start with a declaration like:

constexpr char const* p = "abcde";

This pointer has some value. Let's say N. The crucial thing is - just about anything you can do to try to observe N during constant evaluation would be ill-formed. You cannot 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

We can say for sure that p != q because wherever it is they point, they are clearly different. But we cannot say which one goes first. Such a comparison is undefined behavior, and undefined behavior is disallowed in constant expressions.

You can really only compare to pointers within the same array:

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

Wherever it is that p points to, we know that a points after it. But we don't need to know N to determine this.

As a result, the actual value N during constant evaluation is more or less immaterial.

"abcde" is... somewhere. "abcde"+1 points to one later than that, and has the value "bcde". Regardless of where it points, you can compare it to a null pointer (0 is a null pointer constant) and it is not a null pointer, hence that comparison evaluates as false.

This is a perfectly well-formed constant evaluation, which gcc 6.3 happens to reject.


Although we simply state by fiat that std::less()(p, q) provides some value that gives a consistent total order at compile time and that it gives the same answer at runtime. Which is... an interesting conundrum.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thank you for your answer! Could you please add a reference to the standard which confirms that it's ill-formed to compare p > q? – embedc Jan 30 '19 at 10:12
  • We don't actually say that. We only say that the function call operator is `constexpr`, which is approximately meaningless since we don't actually say *for which arguments* it is a constant subexpression. – T.C. Jan 30 '19 at 13:50
  • @Barry What do you mean by saying that " the value of a string literal like "abcde" is not known until runtime"? I suppose it IS known and e.g. we can compute any substring of it during compile time. Right? – embedc Feb 02 '19 at 09:06
  • @embedc I am paraphrasing half a dozen other comments posted across various answers - which suggested that the value of the _pointer_ (the specific address) is not known until runtime. And then I'm explaining why that does not mean that you cannot use the pointer at all. – Barry Feb 02 '19 at 14:04
  • Well, by saying "SINCE the value of a string literal like "abcde" is not known until runtime" you made it sound like the value of a string literal like "abcde" is really unknown until compile time. – embedc Feb 03 '19 at 10:35