本文是小编为大家收集整理的关于为什么QList::at()不检查索引是否存在,而且还返回一个只读的值?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。
问题描述
这个问题更像是一个询问,然后实际寻求解决问题的解决方案.
QList::at()不仅不检查索引是否超出界限,而且还返回a const,因此仅在read-only方案中使用:
const t&qlist :: at(int i)const
在列表中的索引位置i返回项目. 我必须是一个有效的 列表中的索引位置(即0 <= i
此功能非常快(恒定时间).
我刚刚发现了QList的这些特殊的实现详细信息,同时试图将新值分配给列表中的元素,我想知道是否有这样做的理由.
否检查索引有效性
in C++ STL如果我们服用std::array或std::vector(我不服用std::list,因为它根本没有std::list::at()),我们有以下内容:
std :: vector :: at
该函数会自动检查n是否在向量中有效元素的范围内,如果不是
,则抛出out_of_range异常
首先,我认为不包括检查,以确保"此功能非常快(恒定时间)".但是,在查看了QList的实现之后,我必须说,即使包括支票的恒定时间(和高速)也可以确保.
从范围中进行检查将需要两件事(据我所知):
- 检查给定的索引是否<0-这是一个恒定的时间检查(O(1)如果我们使用一些古怪的符号).显然,我们不能有负索引(我们可以在Python中,但它具有另一个含义;)
-
检查给定的索引是否为QList::size() - 在此处也存在恒定时间,因为实现了QList::size()是:
inline int size() const Q_DECL_NOTHROW { return d->end - d->begin; }
这再次是O(1)(如果我没记错的话),因为两个值都存储在QList::d参数中,这是指向
的指针struct Data { QtPrivate::RefCount ref; int alloc, begin, end; void *array[1]; };
因此,访问这些并计算它们的差异并不会损害其性能太多(显然它会产生一些较小的影响,因为它引入了几个访问和算术操作,并备份并备份并恢复堆栈,因为QList::size()内部的跳跃功能).
那么,为什么要倾倒索引有效性的支票?性能影响实际上是那么大吗?
返回只读值
这样做QList::at()函数提供了一个隐藏和(OMHO)的功能,这在使用它方面有所不同,QList::[]更加令人困惑.另外,C++ STL std::vector和std::array的C++ STL允许使用此函数分配新值.
推荐答案
有" 3种"方法从QList中访问其索引:
const T& QList::at(int) const T& QList::operator[](int) const T& QList::operator[](int) const
如果您查看文档,您会发现最后一个只是:
与()相同.此函数在恒定时间内运行.
因此,我们留下了第一个(at)和非const operator[].这是问题,第二个文档告诉您 1 :
如果此功能在当前共享的列表上调用,则将触发所有元素的副本.否则,此功能在恒定时间内运行.如果您不想修改列表,则应使用QList::at().
注意:对QVector也是如此:
请注意,使用非const操作员可能会导致QVector进行深层副本.
1 对于QList,实际上仅适用于 qt 5 ,不是 QT 4 (至少在文档中),但是对于QVector,对于 qt 4 ,因此.at方法可能在所有QT容器之间保持一致.
这意味着您不能直接在非consst共享QList或非const QVector上使用operator[],您将需要执行以下操作:
QList<T> &mySharedQList = ...; const QList<T> &myConstRef = mySharedQList; myConstRef[0]; // Only ways not to copy the original QList
我的猜测是,.at的目的是为您提供一种保证持续访问时间的方法,无论QList或QVector是什么(operator[])不能保证).您不需要标准库容器的这种方法,因为非const operator[]已经保证在恒定时间内运行(它们永远不会制作副本).
关于索引的检查,这可能是为了保留operator[]的行为,因为与std::vector不同,您可能只想在任何地方使用.at,您只需要读取数据.
问题描述
This question is more of an inquiry then actually seeking a solution for a problem.
QList::at() not only doesn't check if an index is out of bounds but it also returns a const hence it can be used only in a read-only scenario:
const T &QList::at(int i) const
Returns the item at index position i in the list. i must be a valid index position in the list (i.e., 0 <= i < size()).
This function is very fast (constant time).
I've just found out these peculiar implementation details of QList while trying to assign a new value to an element in my list and I'm wondering if there is a reason for doing that.
No check for index validity
In C++ STL if we take std::array or std::vector (I'm not taking std::list since it doesn't have std::list::at() at all) we have the following:
std::vector::at
The function automatically checks whether n is within the bounds of valid elements in the vector, throwing an out_of_range exception if it is not
At first I thought that the check is not included to ensure the "This function is very fast (constant time)." however after looking at the implementation of QList I have to say even if the check was included a constant time (and high speed) is most certainly ensured.
A check for out of bounds would require two things (as far as I know):
- Check if given index is < 0 - this is a constant time check (O(1) if we use some wacky notation). Obviously we can't have negative index (we can in Python but it has another meaning ;))
Check if given index is < QList::size() - constant time is also present here since the way QList::size() is implemented is:
inline int size() const Q_DECL_NOTHROW { return d->end - d->begin; }
This is again O(1) (if I'm not mistaken) since both values are stored inside the QList::d parameter which is a pointer to
struct Data { QtPrivate::RefCount ref; int alloc, begin, end; void *array[1]; };
so accessing those and calculating their difference is not hurting the performance that much (obviously it has some minor impact since it introduces a couple of access and arithmetic operations plus backing up and restoring the stack due to the jump inside the QList::size() function).
So why dump the check for index validity? Is the performance impact actually that big?
Returning a read-only value
By doing so the QList::at() function offers a hidden and (omho) not very useful feature which makes the difference in using it and QList::[] even more confusing. Also the C++ STL equivalent for std::vector and std::array allows assignment of new values using this function.
推荐答案
There are "3" ways of accessing an item from its index in a QList:
const T& QList::at(int) const T& QList::operator[](int) const T& QList::operator[](int) const
If you look at the doc, you will see that the last one is simply:
Same as at(). This function runs in constant time.
So we are left with the first one (at) and the non-const operator[]. Here is the problem, the documentation of the second one tells you that1:
If this function is called on a list that is currently being shared, it will trigger a copy of all elements. Otherwise, this function runs in constant time. If you do not want to modify the list you should use QList::at().
Note: This is also true for QVector:
Note that using non-const operators can cause QVector to do a deep copy.
1 For QList, this is actually only true for Qt 5, not for Qt 4 (at least in the documentation), but for QVector it was already true for Qt 4, so the .at method was probably made consistent between all Qt containers.
This means that you cannot use operator[] directly on a non-const shared QList or on a non-const QVector, you would need to do something like:
QList<T> &mySharedQList = ...; const QList<T> &myConstRef = mySharedQList; myConstRef[0]; // Only ways not to copy the original QList
My guess is that the purpose of the .at is to provide you with a method that guarantees a constant access time, whatever the QList or QVector is (which operator[] does not guarantee). You do not need such method for standard library containers because non-const operator[] are already guaranteed to run in constant time (they are never going to make a copy).
Regarding the check on the index, this is probably to keep the behavior of the operator[] since, unlike std::vector, you probably want to use .at everywhere you only need to read data.