在 Qt 中正确使用 C++11 基于范围的 for 循环

2021-12-09 00:00:00 qt c++ c++11

根据这个演讲在 Qt 容器上使用 C++11 范围基础 for 时的陷阱.考虑:

According to this talk there is a certain pitfall when using C++11 range base for on Qt containers. Consider:

QList<MyStruct> list;

for(const MyStruct &item : list)
{
    //...
}

根据谈话,陷阱来自隐式共享.在引擎盖下,基于范围的 for 从容器中获取迭代器.但是因为容器不是 const,所以 interator 将是非常量的,这显然足以让容器分离.

The pitfall, according to the talk, comes from the implicit sharing. Under the hood the ranged-based for gets the iterator from the container. But because the container is not const the interator will be non-const and that is apparently enough for the container to detach.

当您控制容器的生命周期时,这很容易解决,只需将 const 引用传递给容器以强制它使用 const_iterator 而不是分离.

When you control the lifetime of a container this is easy to fix, one just passes the const reference to the container to force it to use const_iterator and not to detach.

QList<MyStruct> list;
const Qlist<MyStruct> &constList = list;

for(const MyStruct &item : constList)
{
    //...
}

但是,例如容器作为返回值.

However what about for example containers as return values.

QList<MyStruct> foo() { //... }

void main()
{
    for(const MyStruct &item : foo())
    {
    }
}

这里发生了什么?容器还在复制吗?直觉上我会说这是为了避免可能需要这样做?

What does happen here? Is the container still copied? Intuitively I would say it is so to avoid that this might need to be done?

QList<MyStruct> foo() { //... }

main()
{ 
    for(const MyStruct &item : const_cast<const QList<MyStruct>>(foo()))
    {
    }
}

我不确定.我知道它有点冗长,但我需要这个,因为我在大型容器上大量使用基于范围的 for 循环,所以谈话对我来说是正确的.

I am not sure. I know it is a bit more verbose but I need this because I use ranged based for loops heavily on huge containers a lot so the talk kind of struck the right string with me.

到目前为止,我使用辅助函数将容器转换为常量引用,但如果有更简单/更短的方法来实现相同的目标,我希望听到它.

So far I use a helper function to convert the container to the const reference but if there is a easier/shorter way to achieve the same I would like to hear it.

推荐答案

template<class T>
std::remove_reference_t<T> const& as_const(T&&t){return t;}

可能会有所帮助.由于非常量迭代,返回右值的隐式共享对象可以隐式检测写入分片(和分离).

might help. An implicitly shared object returned an rvalue can implicitly detect write-shraring (and detatch) due to non-const iteration.

这给你:

for(auto&&item : as_const(foo()))
{
}

它允许您以 const 方式(而且非常清楚)进行迭代.

which lets you iterate in a const way (and pretty clearly).

如果你需要引用生命周期延长来工作,有 2 个重载:

If you need reference lifetime extension to work, have 2 overloads:

template<class T>
T const as_const(T&&t){return std::forward<T>(t);}
template<class T>
T const& as_const(T&t){return t;}

但是迭代 const 右值并关心它通常是一个设计错误:它们是丢弃的副本,为什么编辑它们很重要?如果你基于 const 限定的行为非常不同,那会在其他地方咬你.

But iterating over const rvalues and caring about it is often a design error: they are throw away copies, why does it matter if you edit them? And if you behave very differently based off const qualification, that will bite you elsewhere.

相关文章