Если у вас есть вектор, содержащий структуры с неинициализированными членами, является ли это неопределенным поведением. Контекстом для этого является вектор, который создается, но затем заполняется в параллельном контексте. Таким образом, инициализация вектора по умолчанию теряется.
Пример
template< typename T >
struct uninitialized
{
T t;
uninitialized(){};
uninitialized & operator=( T const & inT ){ t = inT ; return *this; }
uninitialized & operator=( T && inT ){ t = std::move( inT ); return *this; }
};
std::vector< uninitialized< int > > example()
{
auto vector = std::vector< uninitialized< int > >(500);
// Other code unrelated to vector
for( auto & element : vector ) element = 0; // For loop as stand in for potentially parallel context
// "0" is simply used as a stand in to keep the example simple
// Code which might use vector
return vector;
}
Стоит отметить, что неинициализированные члены никогда не будут прочитаны до того, как они в конечном итоге будут инициализированы.
Если это имеет значение для ответа, предполагается использование простых числовых типов, таких как int
, или агрегатных типов, состоящих из этих числовых типов.
(Также, чтобы избежать ненужных комментариев, reserve
+ push_back
здесь неуместно, поскольку важен окончательный порядок вектора)
В опубликованном коде нет неопределенного поведения (UB):
Создание vector
с 500 элементами — это нормально:
auto vector = std::vector<uninitialized<int>>(500);
Все элементы будут созданы с помощью конструктора uninitialized
по умолчанию. Тот факт, что он не инициализируется T t
, здесь не имеет значения.
Цикл с присваиваниями тоже в порядке:
for( auto & element : vector ) element = 0;
Все элементы будут назначены через uninitialized::operator=(T const & inT )
, а их T t
будет установлен в 0
.
У вас может быть UB только в том случае, если вы попытаетесь прочитать инициализированное значение, но вы упомянули, что не будете этого делать, так что все будет в порядке.
@j6t интересно. Я думаю, у вас есть точка зрения (но она не имеет отношения к опубликованному коду ОП).
В вашем коде нет UB, поскольку перед доступом к элементу данных вы назначаете его в цикле for.
Вы можете упростить свой код и сделать его еще более компактным, добавив параметризованный ctor. Таким образом, вам вообще не придется использовать цикл for для присваивания, и вы сможете просто инициализировать vector
напрямую при его определении, как показано ниже.
template< typename T >
struct uninitialized
{
T t;
uninitialized(){};
uninitialized & operator=( T const & inT ){ t = inT ; return *this; }
uninitialized & operator=( T && inT ){ t = std::move( inT ); return *this; }
//parameterized ctor added
uninitialized(T t_): t(t_){}
};
std::vector< uninitialized< int > > example()
{
//directly initalized vector
std::vector< uninitialized< int > > vectorobj(500, 0);
//no need for for loop here anymore
return vectorobj;
}
Просто из любопытства: что, если элемент отодвинут назад и необходимо перераспределение до инициализации векторных элементов? Будет ли это неопределенным поведением? На этом этапе существующие неинициализированные элементы должны быть скопированы в новое место и, следовательно, должны быть прочитаны.