A partir de C ++ 17, std::array<T,N>::begin() es constexpr :

constexpr iterator begin() noexcept;

Pero, ¿cómo se puede saber el retorno de begin en tiempo de compilación? Por ejemplo:

int main() {
  auto p = std::make_unique<std::array<int,2>>();
  auto it = p->begin();
}

Es un código perfectamente legal (aunque quizás un poco inútil). El inicio de la matriz subyacente y, por lo tanto, el iterador dependen de la dirección mal asignada.

Tengo la sensación de que estoy bajo una interpretación errónea de lo que constexpr hace lo que no veo cómo cualquier función de miembro no estático podría ser constexpr, especialmente si accede (transitivamente) a los miembros de datos.

4
bitmask 27 abr. 2020 a las 17:08

3 respuestas

La mejor respuesta

Las funciones constexpr se pueden invocar en expresiones constantes de tiempo de compilación. Dichas llamadas se evalúan en tiempo de ejecución. Solo cuando la función constexpr se llama en una expresión constante de tiempo de compilación, la función se evalúa en tiempo de compilación.

Pero, ¿cómo puede conocerse el retorno de comenzar en tiempo de compilación?

Se puede conocer en tiempo de compilación cuando la matriz en sí es una constante de tiempo de compilación.

El hecho de que no se pueda conocer en tiempo de compilación cuando la matriz no es una constante de tiempo de compilación no es un problema porque la función se ejecuta en tiempo de ejecución en ese caso.

6
eerorika 27 abr. 2020 a las 14:21

El problema que resuelve este cambio es la capacidad de usar un no -const std::array dentro de una función constexpr para calcular algún resultado. Recuerde que las funciones constexpr no pueden llamar a funciones que no sean constexpr cuando se evalúan en tiempo de compilación.

Como ejemplo, considere una función que calcule la suma de i = 1 a N. Y, por el bien del argumento, considere una forma realmente estúpida de hacerlo (existen ejemplos no estúpidos en el mundo real, pero son más complicados que esto): Cree un matriz inicializada con {1, 2, 3, ...} y luego devuelve la suma de los elementos de array.

// Compute sum from i = 1 to N
template <unsigned N>
unsigned
sum() noexcept
{
    std::array<unsigned, N> a{};
    unsigned u = 1;
    for (auto i = a.begin(); i != a.end(); ++i, ++u)
        *i = u;
    u = 0;
    for (auto const x : a)
        u += x;
    return u;
}

Esto funciona bien y se puede llamar así:

auto x = sum<5>();  // x == 15

Ahora alguien dice: ¡Oigan, calculemos esto en tiempo de compilación!

En C ++ 17, esto es tan simple como colocar constexpr en sum y x:

// Compute sum from i = 1 to N
template <unsigned N>
constexpr
unsigned
sum() noexcept
...

constexpr auto x = sum<5>();  // x == 15

Pero en C ++ 14 esto no compila:

test.cpp:24:20: error: constexpr variable 'x' must be initialized by a constant expression
    constexpr auto x = sum<5>();
                   ^   ~~~~~~~~
test.cpp:11:21: note: non-constexpr function 'begin' cannot be used in a constant expression
    for (auto i = a.begin(); i != a.end(); ++i, ++u)
                    ^

Y la razón por la que no se compila es porque array<T, N>::begin() no es una función constexpr.

1
Howard Hinnant 27 abr. 2020 a las 15:24

No necesita el puntero único para ver el efecto del tiempo de compilación frente a la evaluación del tiempo de ejecución. Esto compila:

#include <array>

int main() {
  static constexpr std::array<int,2> p{1,2};
  constexpr auto it = p.begin();
}

Pero esto no:

#include <array>

int main() {
  std::array<int,2> p{1,2};
  constexpr auto it = p.begin();
}

Error:

<source>:5:18: error: constexpr variable 'it' must be initialized by a constant expression

  constexpr auto it = p.begin();    
                 ^    ~~~~~~~~~    
<source>:5:18: note: pointer to subobject of 'p' is not a constant expression    
<source>:4:21: note: declared here    
  std::array<int,2> p{1,2};

Hablando descuidadamente, el constexpr de begin() significa que es posible evaluar el método en tiempo de compilación en un objeto constexpr. Para la matriz no constexpr, el método se evalúa en tiempo de ejecución. Por lo tanto, p.begin() no se puede utilizar para inicializar constexpr it.

2
idclev 463035818 27 abr. 2020 a las 14:26