error while trying to compile .data() from std::array as a constexpr function in c++20

151 views Asked by At

I was trying to compute an array in compilation time to speed up some functions while i encountered an error which I was unable to resolve with the help of cppreference.

The code boils down to this:

#include <cstddef>
#include <array>

template<typename T, size_t size>
constexpr auto giveArray()
{
    std::array<T, size> arr;
    for(size_t i = 0; i < size; ++i)
        arr[i] = 0;
    return arr;
}

constexpr auto arr = giveArray<int,10>().data();

While compiling with: "$ g++ -std=c++20 code.cpp" on ubuntu i get the error that .data() is not a constexpr function while it definitely is. Why am i getting this error and how it can be fixed while still running this function in compilation time and storing only a pointer, not the std::array object?

3

There are 3 answers

2
François Andrieux On BEST ANSWER

To initialize a constexpr variable you need an initializer that is a constant expression. But giveArray<int,10>().data(); is not a constant expression.

From here :

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value,
  • if the value is of pointer-to-member-function type, it does not designate an immediate function, and
  • if the value is an object of class or array type, each subobject satisfies these constraints for the value. An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.

The expression giveArray<int,10>().data(); has the value category of prvalue , so it is not a glvalue expression. That means it has to be "a prvalue core constant expression whose value satisfies the following constraints" to be a constant expression. Since the expression evaluates to a pointer type, it has to meet this condition :

  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value

In this case, it is none of those things so it is not a constant expression. However, if you change the code to like :

const auto my_array = giveArray<int,10>();
constexpr auto arr = my_array.data();

my_array is a global variable, which implies it has static storage duration. data() returns a pointer to that storage, which makes it a constant expression.

In short, this rule makes it so you can't have a pointer with an invalid pointer value when executing code at compile time.

0
Nicol Bolas On

storing only a pointer, not the std::array object?

You can't.

The rules of C++ are not suspended just because your code executes at compile time (indeed, not suspending those rules is half the point of constexpr code). The array needs to exist in order for a pointer to it to be meaningful. That means the array object needs to continue to be around when someone uses that pointer.

Getting a pointer to a temporary that gets destroyed will leave that pointer pointing to a destroyed object. This is just as true in compile-time code as runtime code. And using that pointer is just as non-functional at compile-time as it would be at runtime. Indeed, it's less functional, as UB (which is what accessing that pointer will provoke) at compile-time must be diagnosed and turned into a compile error.

0
max66 On

You can try with

constexpr auto a0 = giveArray<int,10>();
constexpr auto arr = a0.data();

I mean... whith

constexpr auto arr = giveArray<int,10>().data();

you try to initialize arr with a pointer to memory that is set free immediately after.

You can solve fixing the std::array received from giveArray()

constexpr auto a0 = giveArray<int,10>();

so arr receive a pointer to a memory that remain stable after the initialization

constexpr auto arr = a0.data();