Ответ 1
Итак, позвольте боковому шагу std::array
сделать это немного проще:
template <typename T, size_t N>
struct array {
T elems[N];
constexpr T const* begin() const { return elems; }
};
void foo() {
constexpr array<int,3> v{{1, 2, 3}};
constexpr auto b = v.begin(); // error
}
constexpr array<int, 3> global_v{{1, 2, 3}};
constexpr auto global_b = global_v.begin(); // ok
Почему b
ошибка, а global_b
нормально? Аналогично, почему b
стало бы в порядке, если бы мы объявили v
static constexpr
? Основная проблема заключается в указателях. Чтобы иметь постоянное выражение указателя, он должен всегда указывать на одну известную постоянную вещь. Это на самом деле не работает для локальных переменных без статической длительности хранения, поскольку они имеют принципиально изменяемый адрес. Но для статических или глобальных локальных функций они имеют один постоянный адрес, так что вы можете взять на них постоянный указатель.
В стандарте, из [expr.const]/6:
Константное выражение - это либо ключевое постоянное выражение glvalue, которое относится к объекту, который является разрешенным результатом постоянного выражения (как определено ниже), либо базовое постоянное выражение prvalue, значение которого удовлетворяет следующим ограничениям:
- если значение является объектом типа класса, [...]
- если значение имеет тип указателя, оно содержит адрес объекта со статической продолжительностью хранения, адрес после конца такого объекта ([expr.add]), адрес функции или значение нулевого указателя и
- [...]
b
- это не то, что во второй пуле, так что это не получается. Но global_b
удовлетворяет условие жирного - как было b
, если v
были объявлены static
.