for 循环内的迭代器初始化是否被认为是不好的风格,为什么?

发布于 2024-07-07 18:22:44 字数 1123 浏览 7 评论 0原文

通常,您会发现如下所示的 STL 代码:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

但我们实际上建议这样编写:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
for (; Iter != IterEnd; ++Iter)
{
}

如果您担心范围界定,请添加大括号:

{
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
    for (; Iter != IterEnd; ++Iter)
    {
    }
}

这应该会提高速度和效率,尤其是在您对控制台进行编程时,因为 .end() 函数不会在循环的每次迭代中调用。 我只是认为性能改进是理所当然的,这听起来很合理,但我不知道有多少,而且它肯定取决于容器的类型和实际使用的 STL 实现。 但在使用这种风格几个月后,我实际上更喜欢它而不是第一种。

原因是可读性:for 行干净整洁。 使用实际生产代码中的限定符和成员变量,如果您使用第一个示例中的样式,则很容易使行真正很长。 这就是为什么我在这个例子中故意让它有一个水平滚动条,这样你就能明白我在说什么。 ;)

另一方面,您突然将 Iter 变量引入 for 循环的外部范围。 但是,至少在我工作的环境中,即使在第一个示例中,Iter 也可以在外部范围内访问。

你对此有什么看法? 除了可能限制 Iter 的范围之外,第一种风格还有什么优点吗?

Typically you will find STL code like this:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

But we actually have the recommendation to write it like this:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
for (; Iter != IterEnd; ++Iter)
{
}

If you're worried about scoping, add enclosing braces:

{
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
    for (; Iter != IterEnd; ++Iter)
    {
    }
}

This is supposed to give a speed and efficiency gain, especially if you are programming consoles, because the .end() function is not called on each iteration of the loop. I just take the performance improvement for granted, it sounds reasonable but i don't know how much and it certainly depends on the type of container and actual STL implementation in use. But having used this style for a couple months now i actually prefer it over the first anyway.

The reason being readability: the for line is neat and tidy. With qualifiers and member variables in real production code it is quite easy to have really long for lines if you use the style in the first example. That's why i intentionally made it to have a horizontal scrollbar in this example, just so you see what i'm talking about. ;)

On the other hand, you suddenly introduce the Iter variables to the outer scope of the for loop. But then, at least in the environment i work in, the Iter would have been accessible in the outer scope even in the first example.

What is your take on this? Are there any pro's to the first style other than possibly limiting the scope of Iter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

难得心□动 2024-07-14 18:22:44

如果您正确地将代码包装成行,则内联表单将同样具有可读性。 此外,您应该始终执行 iterEnd = container.end() 作为优化:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
    IterEnd = m_SomeMemberContainerVar.end();
    Iter != IterEnd;
    ++Iter)
{
}

更新:根据 paercebal 的建议修复代码。

If you wrap your code into lines properly, the inline form would be equally readable. Besides, you should always do the iterEnd = container.end() as an optimization:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
    IterEnd = m_SomeMemberContainerVar.end();
    Iter != IterEnd;
    ++Iter)
{
}

Update: fixed the code per paercebal's advice.

假扮的天使 2024-07-14 18:22:44

另一种选择是使用 foreach 宏,例如 boost foreach:

BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
   mangle( item );
}

我知道在现代 C++ 中不鼓励使用宏,但是在 auto 关键字广泛使用之前,这是我发现的获得简洁和可读的东西的最佳方法,并且仍然完全类型安全和快速。 您可以使用任何可以获得更好性能的初始化样式来实现宏。

链接页面上还有一条关于将 BOOST_FOREACH 重新定义为 foreach 以避免烦人的全大写的注释。

Another alternative is to use a foreach macro, for example boost foreach:

BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
   mangle( item );
}

I know macros are discouraged in modern c++, but until the auto keyword is widely available this is the best way I've found to get something that is concise and readable, and still completely typesafe and fast. You can implement your macro using whichever initialization style gets you better performance.

There's also a note on the linked page about redefining BOOST_FOREACH as foreach to avoid the annoying all caps.

懒猫 2024-07-14 18:22:44

如果 for 循环之后不需要迭代器,则第一种形式(在 for 循环内)更好。 它将其范围限制为 for 循环。

我严重怀疑这两种方式是否能提高效率。 还可以使用 typedef 使其更具可读性。

typedef SomeClass::SomeContainer::iterator MyIter;

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

我会推荐较短的名称;-)

The first form (inside the for loop) is better if the iterator is not needed after the for loop. It limits its scope to the for loop.

I seriously doubt that there is any efficiency gain either way. It can also be made more readable with a typedef.

typedef SomeClass::SomeContainer::iterator MyIter;

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

I would recommend shorter names ;-)

归属感 2024-07-14 18:22:44

在 g++ 的 -O2 优化中查看了这一点(只是具体而言),

std::vector、std::list 和 std::map(和朋友)的生成代码没有区别。 std::deque 的开销很小。

所以一般来说,从性能的角度来看,这没有什么区别。

Having looked at this in g++ at -O2 optimisation (just to be specific)

There is no difference in the generated code for std::vector, std::list and std::map (and friends). There is a tiny overhead with std::deque.

So in general, from a performance viewpoint it makes little difference.

小红帽 2024-07-14 18:22:44

不,在循环开始之前保留 iter.end() 不是一个好主意。 如果循环更改容器,则结束迭代器可能会失效。 此外,end() 方法保证为 O(1)。

过早的优化是万恶之源。

而且,编译器可能比你想象的更聪明。

No, it's a bad idea to get a hold on iter.end() before the loop starts. If your loop changes the container then the end iterator may be invalidated. Also, the end() method is guaranteed to be O(1).

Premature optimization is the root of all evil.

Also, the compiler may be smarter than you think.

与之呼应 2024-07-14 18:22:44

尽管迭代器生命周期会让我倾向于 for 作用域版本,但我对其中一种方式没有特别强烈的意见。

然而,可读性可能是一个问题; 这可以通过使用 typedef 来帮助,这样迭代器类型更易于管理:

typedef SomeClass::SomeContainer::iterator sc_iter_t;

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

不是一个巨大的改进,但有一点。

I don't have a particularly strong opinion one way or the other, though iterator lifetime would lean me toward the for-scoped version.

However, readability may be an issue; that can be helped by using a typedef so the iterator type is a bit more manageable:

typedef SomeClass::SomeContainer::iterator sc_iter_t;

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

Not a huge improvement, but a bit.

心是晴朗的。 2024-07-14 18:22:44

我没有任何控制台经验,但在大多数现代 C++ 编译器中,除了范围问题之外,这两个选项最终都是等效的。 即使在调试代码中,Visual Studio 编译器实际上也总是将条件比较放在隐式临时变量(通常是寄存器)中。 因此,虽然逻辑上看起来每次迭代都会进行 end() 调用,但优化的编译代码实际上只进行一次调用,并且比较是循环中每次后续时间执行的唯一操作。

在控制台上可能不是这种情况,但您可以拆开循环来检查优化是否正在发生。 如果是,那么您可以采用您喜欢的或组织中标准的任何风格。

I don't have any console experience, but in most modern C++ compiliers either option ends up being equivilent except for the question of scope. The visual studio compilier will virtually always even in debug code put the condition comparison in an implicit temporary variable (usually a register). So while logically it looks like the end() call is being made through each iteration, the optimized compiled code actually only makes the call once and the comparison is the only thing that is done each subsiquent time through the loop.

This may not be the case on consoles, but you could unassemble the loop to check to see if the optimization is taking place. If it is, then you can you whatever style you prefer or is standard in your organization.

很糊涂小朋友 2024-07-14 18:22:44

它可能会导致代码脱节,但我也喜欢将其提取到一个单独的函数中,并将两个迭代器传递给它。

doStuff(coll.begin(), coll.end())

并且有..

template<typename InIt>
void doStuff(InIt first, InIt last)
{
   for (InIt curr = first; curr!= last; ++curr)
   {
       // Do stuff
   }
 }

喜欢的事情:

  • 不必提及丑陋的迭代器类型(或考虑它是const还是not-const)
  • 如果在每次迭代时不调用end()有好处,我就明白了

永远 就像:

  • 代码
  • 分解额外函数调用的

开销。 但有一天,我们将会拥有 lambda!

It may make for disjointed code, but I also like to pull it out to a separate function, and pass both iterators to it.

doStuff(coll.begin(), coll.end())

and have..

template<typename InIt>
void doStuff(InIt first, InIt last)
{
   for (InIt curr = first; curr!= last; ++curr)
   {
       // Do stuff
   }
 }

Things to like:

  • Never have to mention the ugly iterator type (or think about whether it's const or not-const)
  • If there is gain from not calling end() on each iteration, I'm getting it

Things to not like:

  • Breaks up the code
  • Overhead of additional function call.

But one day, we'll have lambdas!

随遇而安 2024-07-14 18:22:44

我认为这根本不是不好的风格。 只需使用 typedef 即可避免 STL 的冗长和长行。

typedef set<Apple> AppleSet;
typedef AppleSet::iterator  AppleIter;
AppleSet  apples;

for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
   ...
}

斯巴达式编程是减轻风格问题的一种方法。

I don't think it's bad style at all. Just use typedefs to avoid the STL verbosity and long lines.

typedef set<Apple> AppleSet;
typedef AppleSet::iterator  AppleIter;
AppleSet  apples;

for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
   ...
}

Spartan Programming is one way to mitigate your style concerns.

-残月青衣踏尘吟 2024-07-14 18:22:44

如果您担心范围,可以在初始化和循环周围添加大括号。 通常我会做的是在函数的开头声明迭代器并在整个程序中重用它们。

You can throw braces around the initialization and loop if you are concerned about scope. Often what I'll do is declare iterators at the start of the function and reuse them throughout the program.

夏末 2024-07-14 18:22:44

我同意费鲁吉奥的观点。 有些人可能更喜欢第一种样式,以便将 end() 调用拉出循环。

我还可以补充一点,C++0x 实际上会使两个版本更加清晰:

for (auto iter = container.begin(); iter != container.end(); ++iter)
{
   ...
}

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}

I agree with Ferruccio. The first style might be preferred by some in order to pull the end() call out of the loop.

I might also add that C++0x will actually make both versions much cleaner:

for (auto iter = container.begin(); iter != container.end(); ++iter)
{
   ...
}

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}
猫瑾少女 2024-07-14 18:22:44

我通常会写:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
                                   IterEnd = m_SomeMemberContainerVar.end();

for(...)

I would usually write:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
                                   IterEnd = m_SomeMemberContainerVar.end();

for(...)
违心° 2024-07-14 18:22:44

我发现第二个选项更具可读性,因为您最终不会得到一大行。 然而,费鲁吉奥提出了关于范围的一个很好的观点。

I find the second option more readable, as you don't end up with one giant line. However, Ferruccio brings up a good point about the scope.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文