问题描述
我可以看到类被视为调用默认构造函数所需的复杂对象:
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();
}
...
}
但目前尚不清楚为什么需要在QVector的隐藏区域中构造对象。我的意思是这些对象根本无法访问,那么为什么不只保留内存而不是创建真实的对象呢?
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?
作为奖励问题,我想问,如果我想拥有一组非默认可收缩对象,可以安全地将 QVector< T>
替换为 QVector< Wrapper< T> ;
?其中 Wrapper
是这样的:
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() {}
};
推荐答案
制作 QVector 用于非默认可构造类型T:
It's easy enough to make the QVector
work for a non-default-constructible type T:
#define QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(Type) \
template <> QVector<Type>::QVector(int) = delete; \
template <> void QVector<Type>::resize(int newSize) { \
Q_ASSERT(newSize <= size()); \
detach(); \
} \
template <> void QVector<Type>::defaultConstruct(Type*, Type*) { Q_ASSERT(false); }
宏必须在 MyType 声明-在头文件中(如果有的话),并且必须在名称空间或全局范围内:
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:
struct MyType { ... };
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(MyType)
struct A {
struct MyType2 { ... };
};
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(A::MyType2);
不,包装程序不正确。它不会破坏
object
成员。它还不提供移动语义,不能防止被默认构造,等等。 hack
工会成员不是必需的。
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. The hack
union member is not necessary. Nothing in a union will be default-constructed for you.
这里是一个更正确的包装器-几乎类似于
std :: optional
。请参阅,以查看有多少细微差别可选
需求:)
Here's a more correct wrapper - it pretty much resembles
std::optional
. See here to see how much nuance an optional
needs :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/vector-nodefault-33380402
template <typename T> class Wrapper final {
union {
T object;
};
bool no_object = false;
void cond_destruct() {
if (!no_object)
object.~T();
no_object = true;
}
public:
Wrapper() : no_object(true) {}
Wrapper(const Wrapper &o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(o.object);
}
Wrapper(Wrapper &&o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(std::move(o.object));
}
Wrapper(const T &o) : object(o) {}
Wrapper(T &&o) : object(std::move(o)) {}
template <class...Args> Wrapper(Args...args) : object(std::forward<Args>(args)...) {}
template <class U, class...Args> Wrapper(std::initializer_list<U> init, Args...args) :
object(init, std::forward<Args>(args)...) {}
operator T& () & { assert(!no_object); return object; }
operator T&& () && { assert(!no_object); return std::move(object); }
operator T const&() const& { assert(!no_object); return object; }
Wrapper &operator=(const Wrapper &o) & {
if (no_object)
::new (&object) T(o);
else
object = o.object;
no_object = false;
return *this;
}
Wrapper &operator=(Wrapper &&o) & {
if (no_object)
::new (&object) T(std::move(o.object));
else
object = std::move(o.object);
no_object = false;
return *this;
}
template<class... Args> T &emplace(Args&&... args) {
cond_destruct();
::new (&object) T(std::forward<Args>(args)...);
no_object = false;
return object;
}
~Wrapper() {
cond_destruct();
}
};
由于赋值运算符是ref限定的,因此不允许赋值给右值,因此恕我直言下列内容无法编译的属性:
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:
Wrapper<int>() = 1 // likely Wrapper<int>() == 1 was intended
这篇关于QVector要求默认构造函数的原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!