I can see that that classes are treated as complex objects which are required for calling default constructor:
void QVector<T>::defaultConstruct(T *from, T *to)
{
if (QTypeInfo<T>::isComplex) {
while (from != to) {
new (from++) T();
}
...
}
But it's not clear why is it needed to construct objects in the 'hidden' area of QVector. I mean these objects are not accessible at all, so why not just to reserve the memory instead of the real object creation?
And as a bonus question, I would like to ask, if I want to have an array of non-default-constractible objects, can I safely replace QVector<T>
with QVector<Wrapper<T>
? where Wrapper
is something like that:
class Wrapper {
public:
union {
T object;
bool hack;
};
Wrapper() {}
Wrapper(const T &t) : object { t } {}
Wrapper(const Wrapper &t) : object { t.object } {}
Wrapper &operator=(const Wrapper &value) {
object = value.object;
return *this;
}
~Wrapper() {}
};
It's easy enough to make the
QVector
work for a non-default-constructible type T:The macro needs to be present right after
MyType
declaration - in the header file (if any), and it must be in namespace or global scope:No, the wrapper is not correct. It doesn't destruct the
object
member. It also doesn't offer move semantics, doesn't protect from being default-constructed, etc. Thehack
union member is not necessary. Nothing in a union will be default-constructed for you.Here's a more correct wrapper - it pretty much resembles
std::optional
. See here to see how much nuance anoptional
needs :)Since the assignment operators are ref-qualified, it disallows assigning to rvalues, so it has the IMHO positive property that the following won't compile: