2

I am getting 'error C3615: constexpr function 'to_array' cannot result in a constant expression' compiler error with VS2017 in the code below:

#include <stdio.h>
#include <array>

template <typename T>
static constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::array<std::uint8_t, sizeof(T)> result {};

    for (std::size_t i{ sizeof(T) }; i != 0 ; --i)
    {
        result[i - 1] = static_cast<uint8_t>(value >> ((sizeof(T) - i) * 8));
    }

    return result;
}

int main()
{
    constexpr uint64_t sample = UINT64_C(0xab28ecb46814fe75);

    //error C3615: constexpr function 'to_array' cannot result in a constant expression
    constexpr auto a = to_array(sample);

    return 0;
}

If theoretically std::array can be constexpr, why am I getting the error here?

EDIT1:

it compiles without the loop:

template <typename T>
static constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::array<std::uint8_t, sizeof(T)> result {};

    //this is OK
    return result;
}

EDIT2:

the full error message with the loop is:

error C3615: constexpr function 'to_array' cannot result in a constant expression
note: failure was caused by an uninitialized variable declaration
note: see usage of 'result'
Alexey Starinsky
  • 3,699
  • 3
  • 21
  • 57

3 Answers3

5

If theoretically std::array can be constexpr, why am I getting the error here?

The link you are providing is dealing with constructing an std::array as part of a constexpr, not doing anything else with it, like you do.

it compiles without the loop

Your error does't have anything to do with the loop itself, as it does directly with using operator[] of std::array inside a constexpr function body.

operator[] of std::array is constexpr since C++17, but it seem MSVC 19 hasn't implemented it even though it's in the spec. So in your case, building with C++17 enabled (compiler flag /std:c++latest) won't directly solve the issue :-(

The good news is that MSVC 20 is conforming just fine :-)

Geezer
  • 5,600
  • 18
  • 31
2

The full error message is:

error C2131: expression did not evaluate to a constant
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of 'std::array<uint8_t,8>::operator []'
note: while evaluating 'to_array(12333367839138578037)'
fatal error C1903: unable to recover from previous error(s); stopping compilation

If we now take a look at the declaration of operator[] we see that it is not constexpr yet in that versions of Microsoft STD implementation:

reference operator[](_In_range_(0, _Size - 1) size_type _Pos)

The workaround is to use a C-array and then convert it into std::array:

template<std::size_t... is, std::size_t n = sizeof...(is)>
constexpr std::array<std::uint8_t, n>
    to_array(std::uint8_t (&arr)[n], std::index_sequence<is...>)
{
    return {arr[is]...};
}

template <typename T>
constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::uint8_t result[sizeof(T)];
    ...
    return to_array(result, std::make_index_sequence<sizeof(T)>{});
}

or

template<typename T, std::size_t... is>
constexpr std::array<std::uint8_t, sizeof(T)> 
    to_array(T value, std::index_sequence<is...>)
{
    return {static_cast<std::uint8_t>(value >> ((sizeof(T) - is - 1) * 8))...};
}

template <typename T>
constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    return to_array(value, std::make_index_sequence<sizeof(T)>{});
}

But you can't use operator[] on the result in constexpr context anyway.

Evg
  • 25,259
  • 5
  • 41
  • 83
0

I ran into this issue today. I decided to try and wrap std::array to see if I could add constexpr support. I had just begun and this happened to work before I had overloaded the [] operator.

template <typename T, unsigned S> struct _array : public std::array<T,S> {};

Using _array in place of std::array now works.