Постоянная инициализация зависимых нелокальных постоянных переменных с плавающей запятой со статической продолжительностью хранения в разных единицах перевода - PullRequest
1 голос
/ 21 мая 2019

Мне интересно, могу ли я рассчитывать на постоянную инициализацию, когда есть зависимость между двумя постоянными нелокальными переменными с плавающей запятой со статической продолжительностью хранения в двух разных единицах перевода - где одна зависит от (инициализируется как [значение]) другое, и где для последнего выполняется постоянная инициализация.Я ищу ответы, обеспечивающие и интерпретирующие соответствующую часть стандарта, в частности стандарт C ++ 11.

// Note: the non-use of constexpr is intended (C++03 compatibility)

// foo.h
struct Foo {
  static const float kValue;
};

// foo.cpp
const float Foo::kValue = 1.5F;

// bar.h
struct Bar {
  static const float kValue;
};

// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue;  // Constant initialization?

// main.cpp
#include "bar.h"
#include <iostream>

int main() { std::cout << Bar::kValue; }
  • Является ли нелокальная (постоянная) переменная Bar::kValue, котораяиметь статическую длительность хранения, инициализированную с помощью постоянной инициализации?(Который, в свою очередь, отвечает, инициализируется ли он статической или динамической инициализацией)

Подробности / исследования моих собственных

[basic.start.init] / 1состояния [ emphasis mine]:

Выполнена постоянная инициализация:

  • , если каждое полное выражение (включая неявные преобразования) появляется винициализатор ссылки со статической или продолжительностью хранения потока является константным выражением (5.19), и ссылка связана с l-значением, обозначающим объект со статической продолжительностью хранения или с временным (см. 12.2);

  • , если объект со статической или потоковой длительностью хранения инициализируется вызовом конструктора, и если полное выражение инициализации является постоянным инициализатором для объекта;

  • , если объектсо статическим или потоковым хранилищем не инициализируется вызовом конструктора и если объект инициализируется значением или каждое полное выражение, котороеppears в его инициализаторе является константным выражением .

В интерпретации из последнего пункта, что Bar::kValue инициализируется посредством постоянной инициализации, если Foo::kValue являетсяпостоянное выражение.Я подозреваю, что могу найти ответ на вопрос, верно ли это в [expr.const], но здесь я застрял.

Ответы [ 2 ]

1 голос
/ 24 мая 2019

const float не соответствует требованиям для того, чтобы быть константным выражением

(Этот ответ основан на @ комментарии Окталиста , поскольку он воздержалсяпревращения в собственный ответ)

В следующем:

// foo.h
struct Foo {
  static const float kValue;
};

// foo.cpp
const float Foo::kValue = 1.5F;

Foo::kValue действительно инициализируется константным выражением посредством постоянной инициализации , но Foo::kValue не является самим постоянным выражением, поскольку оно не является ни целым, ни перечислением, constexpr, ни временным. [expr.const] / 2 состояния [ выделение шахта]:

A условное выражение является основным константным выражением , если оно не включает одно из следующих значений в качестве потенциально вычисляемого подвыражения ( [basic.def.odr] ), но подвыражения логического И( [expr.log.and] ), логическое ИЛИ ( [expr.log.or] ) и условные ( [expr.cond] ) операциикоторые не оцениваются, не считаются [Примечание: перегруженный оператор вызывает функцию.- примечание к концу]:

...

(2.9) : преобразование lvalue в rvalue ( [conv.lval] ) , если только он не применяется к

  • glvalue целочисленного или перечислимого типа, который относится к энергонезависимому константному объекту с предшествующей инициализацией, инициализированному с помощьюконстантное выражение, или
  • glvalue литерального типа, который ссылается на энергонезависимый объект, определенный с constexpr, или который ссылается на подобъект такого объекта, или
  • glvalueлитеральный тип, который относится к энергонезависимому временному объекту, время жизни которого не закончилось, инициализируется постоянным выражением;

Как ни один из подпунктов для (2.9) *Здесь применяется 1070 *, Foo::kValue не является константным выражением.Из [basic.start.init] / 2 (как указано в более ранней стандартной версии вопроса) Bar::kValue не инициализируется с помощью постоянной инициализации , нокак часть динамической инициализации .

Переменные со статической продолжительностью хранения ( [basic.stc.static] ) или продолжительностью хранения потока ( [basic.stc.thread] ) должен быть инициализирован нулями ( [dcl.init] ) перед любой другой инициализацией [ выделяется моя]:

Инициализация константы выполняется:

  • ...
  • , если объект со статическим или потоковым хранением не был инициализирован вызовом конструктора и если каждое полное выражение, которое появляется в его инициализаторе, является константным выражением .

Примечание о " статическом порядке инициализации fiasco "

Обратите внимание, что этот конкретный пример не приводит к риску статического порядка инициализации fiasco, поскольку Foo::kValue инициализируется как средство постоянной инициализации, а Bar::kValue инициализируется как часть динамической инициализации, и первый гарантированно завершится до начала динамической инициализации.

Если первый также будетбыть инициализирован как часть динамической инициализации, инициализация этих двух будет неопределенно упорядочена относительно друг друга (и всех других динамических инициализаций).

Никогда не полагайтесь на факт, однако, что этот конкретный пример имеетчетко определенный порядок инициализации, так как незначительные изменения могут сделать недействительным этот факт:

// foo.h
struct Foo {
  static const float kDummyValue;
  static const float kValue;
};

// foo.cpp
const float Foo::kDummyValue = 1.5F;    // Constant initialization
const float Foo::kValue = kDummyValue;  // (!) Dynamic initialization

// bar.h
struct Bar {
  static const float kValue;
};

// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue;  // (!) Dynamic initialization

// main.cpp
#include "bar.h"
#include <iostream>

int main() { std::cout << Bar::kValue; }

Как и в этом примере модификатора, инициализация Foo::kValue и Bar::kValue последовательно определяется друг относительно друга, что означает Bar::kValue можно инициализировать (со значением "Foo::kValue) до того, как Foo::kValue будет.

1 голос
/ 21 мая 2019

Хмм ... Я бы не стал доверять этому коду, потому что боялся бы статического порядка инициализации фиаско . AFAIK, порядок статической инициализации между различными модулями компиляции не определен. Это значит, что даже тесты не убедили бы меня, что все будет хорошо.

Не вдаваясь в кровавые подробности, я не смог найти в стандарте ничего, что обеспечило бы инициализацию Foo::kValue в foo.cpp до Bar::kValue в bar.cpp. И если порядок неправильный, значение в Foo::kValue будет просто неопределенным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...