#include <iostream>
struct NonConstant{
NonConstant(int v):v_(v){
std::cout<<"NonConstant\n";
}
int v_;
};
struct Constant{
constexpr Constant(int v):v_(v){
if(v_==0){
std::cout<<"Constant\n";
}
}
int v_;
};
NonConstant a = 2; //#1
Constant b = 0; //#2
int main(){
}
Результат будет:
NonConstant
Constant
Я запутался в этом исходе, Becuase, согласно стандартным правилам, #1
не является stati c инициализация, #2
из-за них:
Постоянный инициализатор для переменной или временного объекта o - это инициализатор, полное выражение которого является константным выражением, за исключением того, что если o - объект , такой инициализатор может также вызывать конструкторы constexpr для o и его подобъектов, даже если эти объекты относятся к нелитеральным типам классов.
Постоянная инициализация выполняется, если переменная или временный объект со статусом c или продолжительность хранения потока инициализируется константным инициализатором для объекта. Если постоянная инициализация не выполняется, переменная с длительностью хранения stati c или длительностью хранения потока инициализируется нулем. Вместе инициализация нулем и постоянная инициализация называются stati c initialization ; все остальные инициализации являются динамическими c инициализацией. Все stati c инициализация строго выполняется перед ([intro.races]) любой динамикой c инициализацией .
Конструктор класса NonConstant
не указан constexpr, инициализация NonConstant a = 2;
вызовет конструктор, не являющийся constexpr, для объекта a
, следовательно, инициализация для #1
будет не stati c инициализация, так что это динамическая c инициализация. Напротив, инициализация Constant b = 0;
является инициализацией c stati, поскольку вызываемый конструктор является конструктором constexpr. И правила говорят, что инициализация всех стати c строго выполняется перед любой динамикой c инициализацией . Итак, почему результат подразумевает, что оценка #1
произошла раньше, чем #2
? Если я что-то упустил, поправьте меня.
UPDATE:
В следующих комментариях к этому вопросу кто-то говорит, что, за исключением того, что класс конструктора может быть нелитеральным типом, конструктор constexpr во всех отношениях должен быть допустимым выражением константы ядра , то есть вызов std::cout
сделает конструктор constexpr не основным постоянным выражением. Однако я нашел другую интерпретацию в cppreference , а именно:
Постоянная инициализация выполняется после (до C ++ 14) вместо (начиная с C ++ 14) нуля инициализация статических c и локальных объектов потока и перед всеми остальными инициализациями. Инициализируются только следующие переменные:
- [...]
- Stati c или локальный объект потока типа класса, который инициализируется вызовом конструктора , если конструктором является constexpr и все аргументы конструктора (включая неявные преобразования) являются постоянными выражениями, и если инициализаторы в списке инициализаторов конструктора и инициализаторы фигурных скобок или равных членов класса содержат только константные выражения .
Это не говорит о том, что конструктор constexpr должен быть выражением константы ядра. Пока вызываемый конструктор удовлетворяет требованиям constexpr
и все его аргументы должны быть постоянными выражениями, а инициализатор-член должен быть постоянными выражениями. Итак, #2
действительно является постоянной инициализацией, потому что аргумент 0
является постоянным выражением, а выбранный конструктор, квалифицированный спецификатором constexpr
, и инициализатор члена подчиняются этим правилам, упомянутым в expr.const .