Reputation: 509
Constexpr function that returns std::array<std:tuple<uint32_t, uint32_t, uint32_t>, size_t>
does not work at compile time due to the use of std::make_tuple. Is there any way to overcome this?
When I tried to remove constexpr specifiction. It works correctly. However, the goal of our project is to provide such function evaluation at compile time.
I got the following error:
At calling part:
error: call to non-constexpr function ‘std::tuple<_Elements>& std::tuple<_Elements>::operator=(std::tuple<_Elements>&&) [with _Elements = {unsigned int, unsigned int, unsigned int}]’
At function part:
error: ‘constexpr std::array<std::tuple<unsigned int, unsigned int, unsigned int>, SIZE> GenArrayTuple() [with long unsigned int SIZE = 128]’ called in a constant expression
The code is below.
template<std::size_t SIZE>
constexpr std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE>
GenArrayTuple() {
std::array<std::tuple<uint32_t, uint32_t, uint32_t>, SIZE> array;
for (uint32_t i = 0; i < SIZE; ++i) {
// FIXME constexpr
arr[2*i] = std::make_tuple(i, i * 2, i * 3 + 1);
}
return array;
}
constexpr uint32_t n = 128;
constexpr auto array_tuple = GenArrayTuple<n>();
Upvotes: 2
Views: 2300
Reputation: 72356
There's actually no issue with using std::make_tuple
in a constant expression in C++14 or later, since C++14 changed it to be constexpr
. So it's a valid constant expression, as long as any class constructors used to initialize the tuple's elements evaluate as valid constant expressions (and there are no such constructors when your element types are all scalars like std::uint32_t
).
But take a better look at the error message. The function it complains about is (taking out some details) tuple& tuple::operator=(tuple&&)
. It turns out the assignment operators of std::tuple
are not marked constexpr
in current C++ versions, meaning any assignment of a tuple
object is not a valid constant expression. (cppreference.com notes that they will be marked constexpr
in C++20; this generally reflects changes from a proposal already accepted by the appropriate C++ working group.)
So to work around this, you'll need to initialize the array
all at once, rather than assigning its elements in a loop. Probably the easiest way to do this is with the help of std::make_integer_sequence
:
#include <tuple>
#include <array>
#include <cstdint>
#include <utility>
template <std::uint32_t ... I>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
sizeof...(I)>
GenArrayTuple_helper(std::integer_sequence<std::uint32_t, I...>) {
return { std::make_tuple(I, I * 2, I * 3 + 1) ... };
}
template <std::size_t SIZE>
constexpr std::array<std::tuple<std::uint32_t, std::uint32_t, std::uint32_t>,
SIZE>
GenArrayTuple() {
return GenArrayTuple_helper(std::make_integer_sequence<std::uint32_t, SIZE>{});
}
Upvotes: 2