在Qt 5.2.1中,以下代码结果如何不同?
QVector<int> c;
if (c.cbegin() != c.begin())
{
std::cout << "Argh!" << std::endl;
}
这打印“argh”,但以下没有。
QVector<int> c;
if (c.begin() != c.cbegin())
{
std::cout << "Argh!" << std::endl;
}
请注意,cbegin和begin位置已切换。 但是如果你更改容器状态我的意思是例如push_back中的东西,它可以正常工作。 在我调用容器上的任何可变方法之前,cbegin和cend都是无效的。 这是一个错误或功能吗?
答案 0 :(得分:22)
您观察到的行为与对QVector::begin
和QVector::cbegin
的通话顺序有关。如果对前者的调用发生在对后者的调用之前,那么它们的返回值比较相等,但是如果你颠倒顺序,它们就不会比较相等。这可以通过以下代码证明:
QVector<int> c;
std::cout << static_cast<void const *>(c.begin()) << std::endl;
std::cout << static_cast<void const *>(c.cbegin()) << std::endl;
打印的地址是相同的。但是,如果交换两个打印语句的顺序,则地址将不同。
如果你深入研究两个成员函数的源代码,你会看到
inline iterator begin() { detach(); return d->begin(); }
inline const_iterator cbegin() const { return d->constBegin(); }
进一步追溯,d->begin()
和d->constBegin()
的身体是相同的。所以区别在于第一种情况下对QVector::detach()
的调用。这被定义为
template <typename T>
void QVector<T>::detach()
{
if (!isDetached()) {
#if QT_SUPPORTS(UNSHARABLE_CONTAINERS)
if (!d->alloc)
d = Data::unsharableEmpty();
else
#endif
reallocData(d->size, int(d->alloc));
}
Q_ASSERT(isDetached());
}
可以找到正在进行的解释here。
隐式共享Qt的容器 - 复制对象时,只复制指向数据的指针。修改对象后,它首先会创建数据的深层副本,以便它不会影响其他对象。创建当天的深层副本的过程称为 detach
因为STL样式的迭代器在概念上只是指针,所以它们直接修改底层数据。因此,使用Container::begin()
创建时,非const迭代器会分离,以便其他隐式共享实例不会受到更改的影响。
因此,如果首先调用QVector::begin()
,则会重新分配容器的基础存储,随后对QVector::cbegin()
的调用将返回相同的指针。但是,反转它们并且在发生任何重新分配之前,对QVector::cbegin()
的调用会返回指向共享存储的指针。
答案 1 :(得分:9)
您使用的测试代码与bug report which was filed in 2012非常相似。它被关闭为无效,因为
constBegin和begin不应该被比较。永远。这是不正确的 使用完全(并且可以使用严格的迭代器检查来捕获),所以 这里没什么好解决的。
这是真的。
但函数begin()
为overloaded为
QVector<T>::iterator QVector::begin();
QVector<T>::const_iterator QVector::begin() const;
这是 未指定的bevahior ,因为未指定C ++ ==
运算符的order of evaluation of the operands。在C ++中没有从左到右或从右到左评估的概念。
因此,根据编译器和优化,您最终会得到iterator
版本的begin或const_iterator
版本。