11

我正在使用std::map<std::string, MyClass* >.

我想测试是否my_map.find(key)返回了一个特定的指针。

现在我正在做;

auto iter = my_map.find(key);
if ((iter != my_map.end()) && (iter->second == expected)) {
    // Something wonderful has happened
}

但是,operator *迭代器需要返回一个引用。直觉上我假设它是有效的并且完全初始化?如果是这样,my_map.end()->second将是NULL,并且(因为NULL从未预料到),我可以将我的 if 语句减少为:

if (iter->second == expected)

根据规范,这是否有效?有没有人有这个实现的实际经验?恕我直言,代码变得更加清晰,并且可能会实现微小的性能改进。

4

4 回答 4

12

直觉上我假设它是有效的并且完全初始化?

您不能假定容器末端元素的迭代器是可取消引用的。根据 C++11 标准的第 24.2.1/5 段:

正如指向数组的常规指针保证有一个指针值指向数组的最后一个元素,所以对于任何迭代器类型,都有一个迭代器值指向对应序列的最后一个元素。这些值称为过去值。i定义了表达式的迭代器的值*i称为可解引用的。该库从不假定过去的值是可取消引用的。[...]

于 2013-04-28T20:52:53.643 回答
3

但是,迭代器的运算符 * 需要返回一个引用。直觉上我假设它是有效的并且完全初始化?

您的假设是错误的,取消引用指向容器外部的迭代器将导致 UB。

24.2 迭代器要求 [iterator.requirements]

24.2.1 一般 [iterator.requirements.general]

7 对数据结构进行操作的大多数库算法模板都有使用范围的接口。范围是一对指定计算开始和结束的迭代器。范围 [i,i) 是一个空范围;一般来说,范围 [i,j) 指的是数据结构中的元素,从 i 指向的元素开始,直到但不包括 j 指向的元素。范围 [i,j) 有效当且仅当 j 可从 i 到达。将库中的函数应用于无效范围的结果是未定义的。

于 2013-04-28T20:46:58.780 回答
2

即使不检查规范,您也可以很容易地看到取消引用迭代器end必须是无效的。

一个完全自然的实现(对于的分解标准实现vector<>)是end()字面意义上的内存指针,其值为ptr_last_element + 1,即指向下一个元素的指针值 - 如果有下一个元素。

您不可能被允许取消引用end迭代器,因为它可能是一个指针,最终将指向堆中的下一个对象,或者可能是溢出保护区(因此您将取消引用随机内存),或者超过末尾堆,并且可能在进程的内存空间之外,在这种情况下,取消引用时可能会出现访问冲突异常)。

于 2013-04-29T11:16:57.647 回答
1

如果iter == my_map.end (),则取消引用它是未定义的行为;但你不是在这里这样做。

auto iter = my_map.find(key);
if ((iter != my_map.end()) && (iter->second == expected)) {
    // Something wonderful has happened
}

如果iter != my_map.end()为 false,则表达式 ( iter->second == expected) 的后半部分将不被执行。

阅读“短路评估”。指针的类似有效代码:

if ( p != NULL && *p == 4 ) {}
于 2013-04-28T21:40:17.510 回答