40

I am trying to declare a constexpr pointer initialized to some constant integer value, but clang is foiling all my attempts:

Attempt 1:

constexpr int* x = reinterpret_cast<int*>(0xFF);

test.cpp:1:20: note: reinterpret_cast is not allowed in a constant expression

Attempt 2:

constexpr int* x = (int*)0xFF;

test.cpp:1:20: note: cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression

Attempt 3:

constexpr int* x = (int*)0 + 0xFF;

test.cpp:1:28: note: cannot perform pointer arithmetic on null pointer

Is what I'm trying to do not allowed by design? If so, why? If not, how can I do it?

Note: gcc accepts all of these.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • 2
    Why do you need a constexpr here? Isn't constexpr effectively the same as const if you aren't using a function? – Robert Mason Apr 29 '12 at 04:11
  • 2
    @RobertMason: Well, for instance, if it's a static member of a class, and it's not constexpr, I can't initialize it in-line. – HighCommander4 Apr 29 '12 at 04:13
  • 1
    Maybe a static inline member function would be more appropriate than a data member. – Vaughn Cato Apr 29 '12 at 04:14
  • 1
    @VaughnCato: I'm not sure why it's more appropriate, but that does work - thanks. I am still curious to know the reasoning behind not allowing non-null constexpr pointers – HighCommander4 Apr 29 '12 at 04:17
  • 1
    @HighCommander4: Aren't you allowed to initialize ints inline? Pointers are ints (isn't everything) down below, so even if it's not entirely standard (I don't know, I don't have a copy of the standard on me) you should be able to get most compilers to accept it. – Robert Mason Apr 29 '12 at 04:18
  • 2
    @RobertMason: clang and gcc don't accept in-line initialization of a non-constexpr pointer. I don't know why - I'd like to know the reason for that, too. – HighCommander4 Apr 29 '12 at 04:24

2 Answers2

29

As Luc Danton notes, your attempts are blocked by the rules in [expr.const]/2 which say that various expressions are not allowed in core constant expressions, including:

-- a reinterpret_cast
-- an operation that would have undefined behavior [Note: including [...] certain pointer arithmetic [...] -- end note]

The first bullet rules out your first example. The second example is ruled out by the first bullet above, plus the rule from [expr.cast]/4 that:

The conversions performed by [...] a reinterpret_cast [...] can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply.

The second bullet was added by WG21 core issue 1313, and clarifies that pointer arithmetic on a null pointer is not permitted in a constant expression. This rules out your third example.

Even if these restrictions did not apply to core constant expressions, it would still not be possible to initialize a constexpr pointer with a value produced by casting an integer, since a constexpr pointer variable must be initialized by an address constant expression, which, by [expr.const]/3, must evaluate to

the address of an object with static storage duration, the address of a function, or a null pointer value.

An integer cast to pointer type is none of these.

g++ does not yet strictly enforce these rules, but its recent releases have been getting closer to them, so we should assume that it will eventually fully implement them.

If your goal is to declare a variable for which static initialization is performed, you can simply drop the constexpr -- both clang and g++ will emit a static initializer for this expression. If you need this expression to be part of a constant expression for some reason, you have two choices:

  • Restructure your code so that an intptr_t is passed around instead of a pointer, and cast it to pointer type when you need to (outside of a constant expression), or
  • Use __builtin_constant_p((int*)0xFF) ? (int*)0xFF : (int*)0xFF. This exact form of expression (with __builtin_constant_p on the left-hand-side of a conditional operator) disables strict constant expression checking in the arms of the conditional operator, and is a little-known, but documented, non-portable GNU extension supported by both gcc and clang.
Richard Smith
  • 13,696
  • 56
  • 78
6

The reason is the one given by the (for once, very helpful) error message: reinterpret_cast is not allowed in a constant expression. It's listed as one of the explicit exception in 5.19 (paragraph 2).

Changing the reinterpret_cast into a C-style cast still ends up with the semantical equivalent of a reinterpret_cast, so that doesn't help (and again the message is very explicit).

If you had a way to obtain a pointer with value 0 you could indeed use p + 0xff but I can't think of a way to obtain such a pointer with a constant expression. You could have relied on the null pointer value (0 in a pointer context like you did, or nullptr) having a value of 0 on your implementation but as you've seen yourself your implementation refuses to do that. I think it's allowed to do that. (E.g. implementations are allowed to bail out for most constant expressions.)

Luc Danton
  • 34,649
  • 6
  • 70
  • 114