Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing two constexpr pointers is not constexpr?

I am looking for a way to map types to numeric values at compile time, ideally without using a hash as proposed in this answer.

Since pointers can be constexpr, I tried this:

struct Base{};
template<typename T> struct instance : public Base{};

template<typename T>
constexpr auto type_instance = instance<T>{};

template<typename T>
constexpr const Base* type_pointer = &type_instance<T>;

constexpr auto x = type_pointer<int> - type_pointer<float>; // not a constant expression

Both gcc and clang reject this code because type_pointer<int> - type_pointer<float> is not a constant expression, see here, for instance.

Why though?

I can understand that the difference between both values is not going to be stable from one compilation to the next, but within one compilation, it should be constexpr, IMHO.

like image 284
Rumburak Avatar asked Jan 04 '20 15:01

Rumburak


People also ask

Can constexpr functions call non constexpr functions?

A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.

Is constexpr always evaluated at compile time?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

What is false constexpr?

In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

When to use #define vs constexpr?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.


1 Answers

Subtraction of two non-null pointers which do not point into the same array or to the same object (including one-past-the-array/object) is undefined behavior, see [expr.add] (in particular paragraph 5 and 7) of the C++17 standard (final draft).

Expressions which would have core undefined behavior[1] if evaluated are never constant expressions, see [expr.const]/2.6.

Therefore type_pointer<int> - type_pointer<float> cannot be a constant expression, because the two pointers are to unrelated objects.

Since type_pointer<int> - type_pointer<float> is not a constant expression, it cannot be used to initialize a constexpr variable such as

constexpr auto x = type_pointer<int> - type_pointer<float>;

Trying to use a non-constant expression as initializer to a constexpr variable makes the program ill-formed and requires the compiler to print a diagnostic message. This is what the error message you are seeing is.

Basically compilers are required to diagnose core undefined behavior when it appears in purely compile-time contexts.

You can see that there will be no error if the pointers are to the same object, e.g.:

constexpr auto x = type_pointer<int> - type_pointer<int>;

Here the subtraction is well-defined and the initializer is a constant expression. So the code will compile (and won't have undefined behavior). x will have a well-defined value of 0.


Be aware that if you make x non-constexpr the compiler won't be required to diagnose the undefined behavior and to print a diagnostic message anymore. It is therefore likely to compile.

Subtracting unrelated pointers is still undefined behavior though, not only unspecified behavior. Therefore you will loose any guarantee on what the resulting program will do. It does not only mean that you will get different values for x in each compilation/execution of the code.


[1] Core undefined behavior here refers to undefined behavior in the core language, in contrast with undefined behavior due to use of the standard library. It is unspecified whether undefined behavior as specified for the library causes an (otherwise constant) expression to not be a constant expression, see final sentence before the example in [expr.const]/2.

like image 149
walnut Avatar answered Nov 14 '22 22:11

walnut