Recibo el 'error C3615: la función constexpr' to_array 'no puede dar como resultado un error del compilador de expresión constante' con VS2017 en el código a continuación:

#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;
}

Si teóricamente std :: array puede ser constexpr, ¿por qué estoy? ¿Recibo el error aquí?

EDITAR1:

Se compila sin el bucle:

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;
}

EDITAR2:

El mensaje de error completo con el bucle es:

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'
c++
2
Alexey Starinsky 9 sep. 2018 a las 13:21

3 respuestas

La mejor respuesta

El mensaje de error completo es:

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

Si ahora echamos un vistazo a la declaración de operator[], vemos que todavía no es constexpr en esas versiones de implementación de STD de Microsoft:

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

La solución es usar una matriz C y luego convertirla en 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)>{});
}

O

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)>{});
}

Pero de todos modos no puede usar operator[] en el resultado en el contexto constexpr.

2
Evg 9 sep. 2018 a las 11:23

Me encontré con este problema hoy. Decidí probar y envolver std :: array para ver si podía agregar soporte constexpr. Acababa de comenzar y esto funcionó antes de sobrecargar el operador [].

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

Usar _array en lugar de std :: array ahora funciona.

0
justdoityourself 24 nov. 2018 a las 15:56

Si teóricamente std :: array puede ser constexpr, ¿por qué estoy? ¿Recibo el error aquí?

El enlace que está proporcionando está relacionado con la construcción de un std::array como parte de un constexpr, sin hacer nada más, como usted.

se compila sin el bucle

Su error no tiene nada que ver con el ciclo en sí, como lo hace directamente con el uso de operator[] de std::array dentro de un cuerpo de función constexpr.

operator[] de std::array es constexpr desde C ++ 17, pero parece que MSVC 19 no lo ha implementado a pesar de que es en la especificación Entonces, en su caso, compilar con C ++ 17 habilitado (indicador del compilador /std:c++latest) no resolverá directamente el problema :-(

La buena noticia es que MSVC 20 se está conformando muy bien :-)

4
Geezer 9 sep. 2018 a las 13:02