C++11: Предотвращает ли оператор присваивания тип от типа POD и, следовательно, от глобальной инициализации?
Предыстория: я нахожусь в среде большого кода, где неопределенный порядок, в котором выполняются глобальные конструкторы, проблематичен. Итак, у меня есть собственный класс, который предназначен для задержки инициализации до первого использования. Вся его магия происходит внутри его операторов * и operator-> функций; они единственные, что определены. Он также хранит некоторое состояние внутри себя, чтобы сделать его доступным для функции автоматической инициализации. Это состояние, конечно, должно быть POD, чтобы весь класс был POD, чтобы его можно было полностью настроить до запуска чьего-либо кода, чтобы весь код везде мог использовать все глобальные переменные везде, не опасаясь, что глобальные перемены будут еще не было настроено.
Некоторое время назад кто-то добавил частный, никогда не определенный оператор присваивания, так что типу никогда не будет назначен (он не предназначен для изменения в любом случае). Теперь кто-то еще говорит, что класс не работает, потому что это не POD. Если вместо того, чтобы быть объявленным, но не определенным, я объявляю это как "=delete", я думаю, что это как-то лучше. И действительно, с этим изменением std::is_pod<>::value возвращает true для типа.
Но как оператор присваивания мешает типу быть POD? Я думал, что требования были просто в том, что в нем должны быть только открытые члены данных, никаких виртуальных методов, а также конструктор или деструктор.
И что еще важнее для моей ситуации: препятствует ли наличие никогда не определенного оператора присваивания классу быть инициализированным во время глобальной инициализации вместе со всеми другими глобальными POD?
Сокращенный пример:
struct LazyString {
const char *c_str;
bool has_been_inited;
string *lazy_str_do_not_use_directly;
string &operator*() { return *get(); }
string *operator->() { return get(); }
private:
string *get() {
// The real code uses a mutex, of course, to be thread-safe.
if (!has_been_inited) {
lazy_str_do_not_use_directly = new string(c_str);
has_been_inited = true;
}
return lazy_str_do_not_use_directly;
}
// Does this make the class non-POD?
// If so, does that mean that global variables of this type
// will not be initialized at global-initialization time, that wonderful
// moment in time where no code has yet been run?
void operator=(const LazyString&);
// If I do this instead, it breaks C++03 compatibility, but is this somehow better?
void operator=(const LazyString&) = delete;
};
LazyString lazy = { "lazy" };
int main(int argc, char *argv[]) {
std::cout << *lazy;
}
2 ответа
Предотвращает ли оператор присваивания тип POD?
Да. Тип POD должен быть тривиальным; и так должно быть тривиально копируемым; и поэтому не должно иметь нетривиальных операторов копирования или перемещения.
Любой предоставленный пользователем оператор является нетривиальным, поэтому объявление конструктора копирования делает класс нетривиальным, а следовательно, и не POD.
и, таким образом, глобальная инициализация?
Нет. Любой инстанцируемый тип, POD или нет, может быть глобальной переменной.
ОБНОВЛЕНИЕ: Из комментария вы хотели спросить:
Может ли это быть статически, а не динамически, инициализировано?
Да, поскольку у него есть тривиальный конструктор; до тех пор, пока инициализатор является постоянным выражением. В вашем примере { "lazy" }
является постоянным выражением, так LazyString
может быть статически инициализирован.
Важной особенностью здесь является то, что он имеет тривиальный конструктор, а не то, что это POD. POD означает, что он также отвечает различным другим требованиям, не относящимся к инициализации.
C++ имеет несколько этапов инициализации для нелокальных переменных со статической длительностью хранения (глобальные переменные попадают в эту категорию) - независимо от того, является ли тип POD или нет.
- нулевая инициализация имеет место перед любой другой инициализацией
- происходит постоянная инициализация
- динамическая инициализация
Первые два типа инициализации должны иметь место до динамической инициализации. Динамическая инициализация - это ситуация, когда порядок инициализации может быть трудно установить. Некоторая динамическая инициализация неупорядочена, некоторые упорядочены внутри единицы перевода и т. Д.
Даже если ваша глобальная переменная не является POD, вы можете быть уверены, что нулевая инициализация будет иметь место до любой динамической инициализации.
См. C++11 3.6.2 "Инициализация нелокальных переменных" для получения подробной информации.