cbegin,在Qt 5.2.1中找出空容器的无效结果

时间:2014-08-27 06:07:36

标签: c++ qt c++11

在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都是无效的。 这是一个错误或功能吗?

2 个答案:

答案 0 :(得分:22)

您观察到的行为与对QVector::beginQVector::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版本。