Являются ли элементы массива constexpr компиляцией констант времени?
Является ли фрагмент кода
struct Parameters {
static constexpr int n = 2;
static constexpr double v[n] = {4.0, 5.0};
};
юридический С++ 11? И если да, то константы времени Parameters::v[0]
и Parameters::v[1]
компилируются или просто указатель Parameters::v
сам a constexpr
(что бы это ни значило во время компиляции)?
Как вы можете видеть, я обычно немного запутался в массивах constexpr
и их инициализации в классах/структурах. Пожалуйста, не стесняйтесь отвечать не только на мой конкретный вопрос, но также упомянуть об общих ловушках и т.д. Относительно этой темы.
Ответы
Ответ 1
Я не вижу проблемы с конструкцией. Цитирование С++ 11, [dcl.constexpr]
:
§1 Спецификатор constexpr
должен применяться только к определению переменной, объявлению функции или шаблон функции или объявление статического элемента данных литерала типа (3.9)....
§9. Спецификатор constexpr
, используемый в объявлении объекта, объявляет объект как const
. Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, этот вызов должен быть константой выражение (5.19). В противном случае, или если спецификатор constexpr используется в описании ссылки, каждое полное выражение которое появляется в его инициализаторе, должно быть постоянным выражением. Каждое неявное преобразование, используемое в преобразование выражений инициализатора и каждый вызов конструктора, используемый для инициализации, должен быть одним из тех допускается в постоянном выражении (5.19).
double
- это буквальный тип, а также массив литералов. Это означает, что v[0]
и v[1]
из вашего кода действительно являются постоянными выражениями.
Ответ 2
struct Parameters {
static constexpr int n = 2;
static constexpr double v[n] = {4.0, 5.0};
};
Этот фрагмент сам по себе, безусловно, является законным, насколько я могу судить. В разделе 7.1.5 [dcl.constexpr] из стандарта С++ 11 указано, что
Спецификатор constexpr применяется только к... объявлению статического элемента данных литерала типа
и тип литерала определен в 3.9:
Тип - это буквальный тип, если он:
- скалярный тип; или...
- массив литералов типа
Итак, static constexpr double v[2] = { ... }
, безусловно, действителен, насколько я могу судить.
Что касается элементов массива constexpr
... Я не уверен. Если мы объявим
constexpr double d = Parameter::v[1];
тогда как g++, так и clang компилируют его в порядке, но версия clang не может ссылаться на ссылку undefined на Parameters::v
. Я не знаю, указывает ли это на ошибку Clang или недействительна конструкция.
Ответ 3
struct Parameters {
static constexpr int n = 2;
static constexpr double v[n] = {4.0, 5.0};
};
int main() {
constexpr int a = Parameters::v[0];
return 0;
}
Этот код в gcc 4.8.2 компилируется в следующее:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: c7 45 fc 04 00 00 00 mov DWORD PTR [rbp-0x4],0x4
b: b8 00 00 00 00 mov eax,0x0
10: 5d pop rbp
11: c3 ret
Итак, да, это постоянная времени компиляции.
clang 3.4 производит аналогичный код:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: b8 00 00 00 00 mov eax,0x0
9: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
10: c7 45 f8 04 00 00 00 mov DWORD PTR [rbp-0x8],0x4
17: 5d pop rbp
18: c3 ret
Опять же, это постоянная времени компиляции.
Все было скомпилировано с -O0.
P.S.: Если a объявлено const, то для gcc ничего не меняется, но для clang does, значение 4 не движется напрямую, как если бы была константа времени компиляции.
Если a не объявлено ни const, ни constexpr, то оба компилятора не могут обрабатывать Parameters:: v [0] как константу времени компиляции.