что означает фраза "предшествующая инициализация" в разделе [expr.const]
constexpr int func(int const& rf){
return rf;
}
int main(){
constexpr int value = func(0);
}
Рассмотрим приведенный выше код, переменная value
должен быть инициализирован константным выражением, которое func(0)
, которое в первую очередь должно быть основным постоянным выражением. Чтобы определить, является ли выражениеfunc(0)
является основным постоянным выражением, к нему будут применяться следующие правила, а именно:
expr.const#2.7
Выражение e является основным постоянным выражением, если оценка e, следуя правилам абстрактной машины, не оценила бы одно из следующих выражений:
преобразование lvalue-to-rvalue, если оно не применяется к
[...], или
(2.7.4) энергонезависимое значение glvalue литерального типа, которое относится к энергонезависимому объекту, время жизни которого началось в пределах вычисления e;
Несмотря на lvalue-to-rvalue conversion
применяется к rf
и такое преобразование соответствовало пункту (2.7.4), однако взгляните на следующий абзац, а именно:
expr.const #2.11
выражение id, которое относится к переменной или элементу данных ссылочного типа, если ссылка не имеет предшествующей инициализации и либо
- (2.11.1) он инициализируется постоянным выражением или,
- (2.11.2) его время жизни началось в пределах оценки e;
Я не знаю, что на самом деле фраза preceding initialization
жадный? Означает ли это, что переменная должна быть инициализирована перед ее использованием, или это означает, что в объявлении переменной должен быть инициализатор. Во всяком случае, перед применениемlvalue-to-rvalue conversion
glvalue rf
, glvalue rf
должны быть оценены для определения идентичности объекта, которым управляют:
Glvalue - это выражение, оценка которого определяет идентичность объекта, битового поля или функции.
Это означает, что должен соблюдаться не только пункт [expr.const#2.7], но и [expr.const#2.11].
Потому что id-выражение rf
имеет справочный тип. Итак, чтобы сделать выражениеfunc(0)
быть основным постоянным выражением, id-выражение rf
должен иметь предшествующую инициализацию и удовлетворять по крайней мере одному из пунктов (2.11.1) и (2.11.2). В моем примере пуля (2.11.2) подчиняетсяrf
и компилятор соглашается, что func(0)
является постоянным выражением. Результат здесь, результат, кажется, свидетельствует о том, чтоpreceding initialization
средства be initialized
вместо того, чтобы иметь инициализатор из-за того, что объявление параметра не имеет инициализатора в первом примере.
Чтобы проверить такую мысль, я тестирую следующий код:
constexpr int func(int const& rf){
constexpr int const& v = rf;
return 0;
}
int main(){
static int const data = 0;
constexpr int value = func(data);
}
В результаты компиляторов указываютrf
не является постоянным выражением. Я смущен таким исходом. Согласно сделанному предположению.rf
имеет предшествующую инициализацию, а пуля (2.11.1)
подчиняется, потому что data
является постоянным выражением, даже если пуля (2.11.2)
не устраивает.
Итак, мне интересно, что на самом деле означает фраза preceding initialization
жадный? Если это означает, что объявление для переменной имеет инициализатор, как может выражениеfunc(0)
в первом примере быть постоянным выражением?
2 ответа
Результат, по-видимому, свидетельствует о том, что "предшествующая инициализация" означает "быть инициализирована", а не иметь инициализатор, поскольку в первом примере объявление параметра не имеет инициализатора.
Это действительно означает "быть инициализированным", но что более важно, касается видимости предыдущей инициализации в контексте оцениваемого выражения. В вашем примере в контексте оценкиfunc(0)
у компилятора есть контекст, чтобы увидеть инициализацию rf
с 0
. Однако в контексте оценки только выраженияrf
в func
, он не видит инициализацию rf
. Анализ является локальным, так как не анализирует каждый call-сайт. Это приводит к выражениюrf
сам внутри func
не быть постоянным выражением, покаfunc(0)
является постоянным выражением.
Если бы вы вместо этого написали это:
constexpr int func(int const& rf) {
/* constexpr */ int const& v = rf;
return v;
}
int main() {
static int const data = 0;
constexpr int value = func(data);
}
Это снова нормально, потому что в контексте func(data)
, rf
имеет предшествующую инициализацию постоянным выражением data
, а также v
имеет предшествующую инициализацию с помощью rf
которое не является постоянным выражением, но его время жизни началось в пределах оценкиfunc(data)
.
Это CWG2186:
2186. Неясный момент, что "предшествующая инициализация" должна предшествовать
Подобно проблеме 2166, требование 8.20 [expr.const] bullet 2.7.1 для
- энергонезависимое значение glvalue целочисленного или перечисляемого типа, которое относится к полному энергонезависимому константному объекту с предыдущей инициализацией, инициализированному константным выражением, или
не указывает точку, в которой выполняется определение "предшествующей инициализации": это точка, в которой ссылка на переменную появляется лексически, или это точка, в которой происходит самая внешняя оценка константы? По этому поводу существуют расхождения в реализации.
Но значение должно быть "лексическим", потому что ODR.