Почему std :: array :: begin () constexpr начиная с C ++ 17? - PullRequest
4 голосов
/ 27 апреля 2020

Начиная с C ++ 17, std::array<T,N>::begin() является constexpr:

constexpr iterator begin() noexcept;

Но как можно узнать возвращение begin во время компиляции? Например:

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

Совершенно допустимый код (хотя, возможно, немного бесполезный). Начало базового массива и, следовательно, итератора зависит от адреса mallo c.

У меня есть ощущение, что я неправильно понимаю, что constexpr делает как Я не понимаю, как любая функция-член c, не относящаяся к состоянию, могла бы быть constexpr, особенно если она (транзитивно) обращается к членам данных.

Ответы [ 3 ]

6 голосов
/ 27 апреля 2020

constexpr функции можно вызывать в выражениях, не зависящих от времени компиляции. Такие вызовы оцениваются во время выполнения. Только когда constexpr функция вызывается в выражении с постоянной во время компиляции, функция оценивается во время компиляции.

Но как можно узнать о возвращении начала во время компиляции?

Это может быть известно во время компиляции, когда сам массив является постоянной времени компиляции.

Тот факт, что он не может быть известен во время компиляции, когда массив не является постоянной времени компиляции, не является проблемой потому что в этом случае функция выполняется во время выполнения.

2 голосов
/ 27 апреля 2020

Вам не нужен уникальный указатель, чтобы увидеть влияние времени компиляции на время выполнения. Это скомпилирует:

#include <array>

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

Но это не так:

#include <array>

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

Ошибка:

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

Небрежно говоря, constexpr из begin() означает что можно оценить метод во время компиляции объекта constexpr. Для массива, отличного от constexpr, метод оценивается во время выполнения. Следовательно, p.begin() нельзя использовать для инициализации constexpr it.

1 голос
/ 27 апреля 2020

Проблема, которую решает это изменение, заключается в возможности использовать non -const std::array в функции constexpr для вычисления некоторого результата. Напомним, что constexpr функции не могут вызывать не constexpr функции при оценке во время компиляции.

В качестве примера рассмотрим функцию, которая вычисляет сумму от i = 1 до N. И для аргумента Для этого рассмотрим действительно глупый способ сделать это (в реальной жизни существуют глупые примеры, но они более сложны): создайте массив, инициализированный {1, 2, 3, ...}, а затем возвращающий сумму элементов 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;
}

Это прекрасно работает и может быть названо так:

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

Теперь кто-то говорит: эй, давайте вычислим это во время компиляции!

В C + +17 это так же просто, как наложение constexpr на sum и x:

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

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

Но в C ++ 14 это не компилируется:

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)
                    ^

И причина, по которой он не компилируется, заключается в том, что array<T, N>::begin() не является constexpr функцией.

...