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

R.J*_*R.J 19 c++ qt c++11

在Qt 5.2.1中,以下代码结果如何不同?

QVector<int> c;
if (c.cbegin() != c.begin())
{
   std::cout << "Argh!" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这打印"argh",但以下没有.

QVector<int> c;
if (c.begin() != c.cbegin())
{
    std::cout << "Argh!" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

请注意,cbegin和begin位置已切换.但是如果你更改容器状态我的意思是例如push_back中的东西,它可以正常工作.在我调用容器上的任何可变方法之前,cbegin和cend都是无效的.这是一个错误或功能吗?

Pra*_*ian 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;
Run Code Online (Sandbox Code Playgroud)

打印的地址是相同的.但是,如果交换两个打印语句的顺序,则地址将不同.

如果你深入研究两个成员函数的源代码,你会看到

inline iterator begin() { detach(); return d->begin(); }
inline const_iterator cbegin() const { return d->constBegin(); }
Run Code Online (Sandbox Code Playgroud)

而追溯进一步,两者的机构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());
}
Run Code Online (Sandbox Code Playgroud)

可以在此处找到有关正在发生的事情的说明.

隐式共享Qt的容器 - 复制对象时,只复制指向数据的指针.修改对象后,它首先会创建数据的深层副本,以便它不会影响其他对象.创建当天的深层副本的过程称为detach

因为STL样式的迭代器在概念上只是指针,它们直接修改底层数据.因此,非const迭代器在使用时会分离,Container::begin()以便其他隐式共享实例不会受到更改的影响.

因此,如果调用首先QVector::begin()发生,则重新分配容器的底层存储,并且后续调用QVector::cbegin()将返回相同的指针.但是,QVector::cbegin()在发生任何重新分配之前,将它们反向并调用以返回指向共享存储的指针.


UmN*_*obe 9

您使用的测试代码与2012年提交错误报告非常相似.它被关闭为无效,因为

不应该比较constBegin和begin.永远.这根本不是正确的用法(并且可以通过严格的迭代器检查来捕获),所以这里没有什么可以修复的.

这是真的.但是这个函数begin()重载

 QVector<T>::iterator       QVector::begin();
 QVector<T>::const_iterator QVector::begin() const;
Run Code Online (Sandbox Code Playgroud)

这是一个未指定的bevahior,因为未指定 C++ 运算符的操作数评估顺序==.在C++中没有从左到右或从右到左评估的概念.

因此,根据编译器和优化,您将iterator得到begin或const_iteratorversion的版本.