Безопасно ли создавать и использовать векторы во время статической инициализации?

У меня есть код C ++, который объявляет статические переменные времени жизни, которые инициализируются вызовами функций. Вызываемая функция создает vector экземпляр и называет его push_back метод. Является ли код рискованным из-за фиаско статического порядка инициализации C ++? Если нет, то почему нет?

Дополнительная информация:

  1. Что такое «фиаско статического порядка инициализации»?

    Это объясняется в C ++ FAQ 10.14

  2. Почему я думаю, что использование вектора может вызвать фиаско?

    Возможно, что vector Конструктор использует значение другой статической переменной времени жизни, инициализированной динамически. Если это так, то нет ничего, чтобы гарантировать, что vectorпеременная инициализируется перед использованием vector в моем коде. Инициализация result (см. код ниже) может в конечном итоге вызвать vector конструктор до vectorЗависимости полностью инициализируются, что приводит к доступу к неинициализированной памяти.

  3. Как этот код выглядит в любом случае?

    struct QueryEngine {
    QueryEngine(const char *query, string *result_ptr)
    : query(query), result_ptr(result_ptr) { }
    
    static void AddQuery(const char *query, string *result_ptr) {
    if (pending == NULL)
    pending = new vector<QueryEngine>;
    pending->push_back(QueryEngine(query, result_ptr));
    }
    
    const char *query;
    string *result_ptr;
    
    static vector<QueryEngine> *pending;
    };
    
    vector<QueryEngine> *QueryEngine::pending = NULL;
    
    void Register(const char *query, string *result_ptr) {
    QueryEngine::AddQuery(query, result_ptr);
    }
    
    string result = Register("query", &result);
    

8

Решение

К счастью, static объекты инициализируются нулями еще до любой выполняется другая инициализация (даже до «истинной» инициализации тех же объектов), поэтому вы знаете, что NULL будет установлен на этот указатель задолго до Register сначала вызывается.1

Теперь, с точки зрения работы с вашим вектором, кажется, что (технически) вы мог столкнуться с такой проблемой:

[C++11: 17.6.5.9/3]: Функция стандартной библиотеки C ++ не должна прямо или косвенно изменять объекты (1.10), доступные для потоков, отличных от текущего потока, если к объектам не обращаются прямо или косвенно через неконстантные аргументы функции, включая this,

[C++11: 17.6.5.9/4]: [Заметка: Это означает, например, что реализации не могут использовать статический объект для внутренних целей без синхронизации, потому что это может вызвать гонку данных даже в программах, которые не разделяют объекты явно между потоками. —Конечная записка]

Обратите внимание, что хотя в этой заметке требуется синхронизация, она упоминается в отрывке, в котором в конечном итоге признается, что static детали реализации в противном случае допускаются.

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

1 И это является указатель NULL, каким бы ни было его побитовое представление, а не блот до нуля.

4

Другие решения

vector не зависит от того, что мешает его использованию при динамической инициализации статики. Единственная проблема в вашем коде — это отсутствие безопасности потоков — нет особой причины думать, что вы должны заботиться об этом, если только у вас нет статики, конструкция которой порождает потоки ….

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

Нет … инициализация result звонки AddQuery который проверяет if (pending == NULL) — инициализация до NULL безусловно, будет выполнено до любой динамической инициализации, согласно 3.6.2 / 2:

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

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

Так что даже если result назначение находится в другой единице перевода, это безопасно. См. 3.6.2 / 2:

Вместе нулевая инициализация и постоянная инициализация называются статическая инициализация; все остальные инициализации динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.

2

По вопросам рекламы [email protected]