(Это продолжение этого вопроса.)
Поэтому я хотел бы задать вопрос специально, чтобы понять стандарт, указанный в , который я получил, и мой точный вопрос указан в заголовке.
Честно говоря, даже не на cppreference я понимаю, в чем причина, почему так написано в стандарте.
Однако вот минимальный пример:
#include <array>
int main() {
auto arr = std::array<int,3>{{1,2,3}};
constexpr auto size1 = arr.size(); // OK
auto const& array = arr;
constexpr auto size2 = array.size(); // does not compile
}
который не компилируется с ошибкой (ошибка сообщения одинакова с -std=11
/14
/17
/2a
, отсюда и теги для двух крайностей)
$ g++ -std=c++17 deleteme.cpp && ./a.out
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:6:39: error: the value of ‘array’ is not usable in a constant expression
6 | constexpr auto size2 = array.size(); // does not compile
| ^
deleteme.cpp:5:17: note: ‘array’ was not initialized with a constant expression
5 | auto const& array = arr;
| ^~~~~
но он компилируется, если мы удалим &
.
С другой стороны, если бы я просто полагался на примечание, которое гласит ‘array’ was not initialized with a constant expression
, я бы предположил, что следующее компилируется
#include <array>
int main() {
constexpr auto arr = std::array<int,3>{{1,2,3}};
constexpr auto size1 = arr.size(); // OK
constexpr auto& array = arr;
constexpr auto size2 = array.size(); // does not compile
}
но это не так, и компилятор говорит (ошибка сообщения такая же, как и с -std=11
/14
/17
/2a
)
$ g++ -std=c++17 deleteme.cpp && ./a.out
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:5:29: error: ‘arr’ is not a constant expression
5 | constexpr auto& array = arr;
| ^~~
что в основном означает, что arr
не является «постоянным выражением», даже если это constexpr
, что выглядит как минимум очень плохой формулировкой для меня.
Описанное поведение @StoryTeller-UnslanderMonica одинаково во всех стандартных версиях C++11-C++20.
@ecatmur - Да, но нет. Формулировки становятся все более точными в каждом опубликованном стандарте. Описание поведения почти не отличается.
Связано, но не касается мотивации стандартного поведения: stackoverflow.com/questions/51456712/… stackoverflow.com/questions/54124899/…
Я считаю, что это потому, что правила постоянного выражения позволяют нам использовать оператор адреса, как намекается в этом ответе . Например, это законно:
void f() {
int i, j;
constexpr bool b = &i == &j; // OK, b := false since i and j are distinct objects
}
Может показаться безобидным разрешить использование ссылок внутри константных выражений:
void g() {
int i, j;
int& r = j;
constexpr bool b = &i == &r; // OK, surely?
}
Но тогда мы могли бы сделать референт ссылки зависимым от непостоянной переменной и таким образом внедрить это непостоянное значение в постоянное вычисление:
void h(bool a) {
int i, j;
int& r = a ? i : j;
constexpr bool b = &i == &r; // oops, b := a
}
Должна быть возможность смягчить запрет на оценку ссылок запретом на получение (или использование?) адресов ссылок, но это, безусловно, потребует некоторых усилий, чтобы новый язык имел ожидаемый эффект.
Приложение: Статья P2280R1 "Использование неизвестных ссылок в постоянных выражениях" направлена на то, чтобы разрушить стандарты, которые делают ваш исходный пример неправильным. Он (пока) не вдается в подробности о том, что именно будет разрешено, но кажется вероятным, что формирование или сравнение указателей на «неизвестные» объекты, которые являются референтами ссылок из-за пределов контекста constexpr, будут запрещены, поэтому g()
продолжим быть недействительным, не говоря уже о h()
.
О, я люблю примеры! Однако у меня есть ощущение, что это наблюдение зависит от (поддающегося проверке) факта, что адреса переменных являются constexpr
-подобными (я думаю, есть подходящее слово/прилагательное, но я его не знаю), в том смысле, что они не могут измениться; constexpr bool b = &i == &j
отлично работает, например. Однако при этом constexpr int * b = &i;
не компилируется. Так что дело не в адресах constexpr
-ish, как я думал.
@Enlico в принципе, вам разрешено использовать адреса автоматических переменных в вашем постоянном выражении, если эти адреса не «убегают», то есть не становятся результатом выражения, как описано eel.is/ c++draft/expr.const#11.2
Тем не менее, я мог бы переместить int i, j;
за пределы вышеуказанных функций (в область действия файла) или сделать их static
, и их адреса были бы разрешенными результатами константного выражения.
Большое спасибо. Это многое проясняет!
constexpr
спецификатор, используемый в объявлении переменной, требует, чтобы полное выражение инициализации было постоянным выражением, которое управляется:
Спецификатор constexpr, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь литеральный тип и должен быть инициализирован. В любом объявлении переменной constexpr полное выражение инициализации должно быть константным выражением.
Константное выражение — это либо основное постоянное выражение glvalue, которое ссылается на сущность, являющуюся разрешенным результатом постоянного выражения (как определено ниже), либо основное постоянное выражение prvalue, значение которого удовлетворяет следующим ограничениям.
Сущность является разрешенным результатом константного выражения, если это объект со статической длительностью хранения, который либо не является временным объектом, либо является временным объектом, значение которого удовлетворяет вышеуказанным ограничениям, либо является функцией.
Основное постоянное выражение должно удовлетворять этим правилам:
Выражение e является основным константным выражением, если только вычисление e, следуя правилам абстрактной машины, не будет оценивать одно из следующих выражений:
- [...]
- id-выражение, которое ссылается на переменную или элемент данных ссылочного типа, если ссылка не имеет предшествующей инициализации и либо
- он инициализируется константным выражением или.
- его жизнь началась в пределах оценки e;
В вашем примере id-выражение array
не инициализируется константным выражением из-за того, что arr
не имеет статической длительности хранения, и его время жизни не начинается при оценке инициализации для size2
. Следовательно, инициализация не является постоянным выражением, программа плохо сформирована.
Не могли бы вы решить, из какого стандартного документа вы хотите цитировать? Я отредактировал теги, чтобы они соответствовали указанным вами тестам, но теперь вы добавили тест на C++11.