在映射上迭代时使用++it或it++
++it or it++ when iterating over a map?
显示如何迭代std::map
的示例通常是这样的:
MapType::const_iterator end = data.end();
for (MapType::const_iterator it = data.begin(); it != end; ++it)
。用++it
代替it++
。有什么原因吗?如果我用it++
代替会有问题吗?
it++
返回前一个迭代器的副本。由于没有使用这个迭代器,这是浪费的。++it
返回对自增迭代器的引用,避免了复制操作。
为了进行测试,我制作了三个源文件:
#include <map>
struct Foo { int a; double b; char c; };
typedef std::map<int, Foo> FMap;
### File 1 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(), end = m.end(); it != end; ++it)
it->second = f;
}
### File 2 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(); it != m.end(); ++it)
it->second = f;
}
### File 3 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(); it != m.end(); it++)
it->second = f;
}
### end ###
用g++ -S -O3
, GCC 4.6.1编译后,我发现版本2和3产生相同的汇编,版本1只有一条指令不同,cmpl %eax, %esi
vs cmpl %esi, %eax
。
所以,选择适合你的风格。前缀增量++it
可能是最好的,因为它最准确地表达了您的需求,但不要为此而烦恼。
使用自增前操作符比使用自增后操作符在性能上有一点优势。在设置使用迭代器的循环时,应该选择使用预增量:
for (list<string>::const_iterator it = tokens.begin();
it != tokens.end();
++it) { // Don't use it++
...
}
当您考虑这两个操作符通常如何实现时,原因就一目了然了。预增量非常简单。然而,为了使后增量工作,您需要首先复制对象,在原始对象上执行实际的增量,然后返回副本:
class MyInteger {
private:
int m_nValue;
public:
MyInteger(int i) {
m_nValue = i;
}
// Pre-increment
const MyInteger &operator++() {
++m_nValue;
return *this;
}
// Post-increment
MyInteger operator++(int) {
MyInteger clone = *this; // Copy operation 1
++m_nValue;
return clone; // Copy operation 2
}
}
可以看到,增量后实现涉及两个额外的复制操作。如果所讨论的对象体积庞大,这可能会相当昂贵。话虽如此,一些编译器可能足够聪明,可以通过优化处理单个复制操作。关键是后增量通常会比前增量涉及更多的工作,因此明智的做法是习惯将"++"放在迭代器之前,而不是之后。
(1)链接网站的信用。
从逻辑的角度来看,这是一样的,这里没有关系。
为什么使用前缀式——因为它更快——它改变迭代器并返回它的值,而后缀式创建temp对象,对当前迭代器自增,然后返回temp对象(自增之前同一迭代器的副本)。因为这里没有人观察这个临时对象(返回值),所以它是相同的(逻辑上)。
编译器很有可能会优化这个
另外——实际上,对于任何类型都应该是这样的。但这是应该的。因为任何人都可以重载operator++
——后缀和前缀,它们可能有副作用和不同的行为。
这是一件很可怕的事情,但仍然是可能的。
这不会引起任何问题,但使用++it
更正确。对于小类型,使用++i
或i++
并不重要,但对于"大"类:
operator++(type x,int){
type tmp=x; //need copy
++x;
return tmp;
}
编译器可能会优化掉其中的一些,但很难确定。
正如其他答案所说,除非它在上下文中不起作用,否则更喜欢++它。对于小类型容器的迭代,它实际上没有什么区别(或者如果编译器将其优化掉,则没有区别),但对于大类型容器,由于节省了创建副本的成本,它可以产生区别。
True,在特定的上下文中,您可能知道该类型足够小,因此您不必担心它。但是稍后,您团队中的其他人可能会将容器的内容更改到重要的位置。另外,我认为最好让自己养成一个好习惯,只有当你知道必须这样做时才进行增量操作。
- 为什么我会" void value not ignored as it ought to be"?
- 为什么当我为 for(auto& it : myUnorderedMap) {... = std::move(it.second)} 时,我会得到一个 const 引用?
- Is it good to use SDL_PIXELFORMAT_UNKNOWN?
- 这在C++ "It does not own the underlying data, and so is cheap to copy or assign"中意味着什么
- QString::utf16(): Is it UB or not?
- 我正在"void value not ignored as it ought to be"我该怎么办?
- GotW #88 中的"It doesn’t work for references that are members of objects"是什么意思?
- 如何使用set<pair<int,int> >::iterator itrator it迭代set<pair<int,int> >st中的值?
- CMap with CArray Inside it
- 这个给定的代码应该将给定的数字转换为尽可能滞后的数字,no.by 用 9.It 替换合适的数字是行不通的
- 为什么在使用 auto&&it=--vec.end(),是 UB 时自动推导左值引用?
- 您可以使用C 从在线编译器(repl.it)打开文件吗?
- 如何修复类"Invalid operands to binary expression "类" to "类" "错误 (repl.it)
- 在 std::list 中,std::d istance(it.begin(), std::p rev(it.end()
- 尝试分配函数指针时获取"Void value not ignored as it ought to be"
- 创建结构并传递IT功能
- C++ RVO: when it happens?
- "void value not ignored as it ought to be" - 出了什么问题?
- 在模板中使用类型类型的类型类型使用模板类别IT自我
- 在 Eclipse.It 中运行C++代码时出错,'Your program cannot output..'