【问题标题】:c++: Search in a map without using an iteratorc++:在地图中搜索而不使用迭代器
【发布时间】:2013-06-06 12:17:42
【问题描述】:

考虑一下我有这个:

std::map<int, int> intMap;
intMap[11] = 21; 
intMap[12] = 22; 
intMap[13] = 23; 
intMap[14] = 24; 

int val = 0; 

当我想找到map[11]的值时,如果存在,放入val我正在使用:

std::map<int, int>::iterator it = intMap.find(11);
if(it != intMap.end())  
    val = it->second;

有没有办法在不使用迭代器的情况下做到这一点?我的意思是,更短的方式。

int val = intMap[11] 对我来说不是一个选项,因为它会添加 11 作为 intMap 的键(如果它还不存在),我对此不感兴趣。请参阅here。

【问题讨论】:

  • 我省略了我正在做的检查,已编辑。
  • 这个问题意义不大。如果您知道该元素存在,则可以使用m[x] 或*m.find(x)。否则,即如果你不知道,那么你必须有一种机制来判断元素是否存在。
  • @KerrekSB,请参阅 Golgauth 的回答。
  • @SteveJessop UB 在哪里?我看不出来。 operator[] 将动态初始化一个值,并且在取消引用之前,他正在针对 intMap.end() 测试迭代器。
  • @Arne:问题中的代码已根据我的评论进行了编辑。最初没有针对intMap.end() 的检查(并且由于仍然没有else 子句,我们仍然不知道提问者的代码如何处理这种情况)。

标签: c++ stdmap


【解决方案1】:

在 c++11 中,map 有一个名为 at(Key) 的新方法,如果映射中不存在 Key,它将返回对映射值的引用或抛出异常.

在 c++ 中,您需要有一个中间迭代器。

【讨论】:

  • "c++11" 仍然是 "c++"。此外,您没有必须 有一个中间迭代器。你可以这样做:(map.count(x) ? map[x] : default_value)
  • @karadoc 您执行了两次搜索。这就是为什么我更喜欢迭代器。
  • @AndréPuel:如果插入是可选的,甚至可以有条件地使用lower_bound,然后是暗示插入。总而言之,迭代器在这里做得很好。
  • @AndréPuel 我认为迭代器很适合这项工作;最好不要搜索两次。我只反对使用“需要”这个词。 (挑剔)
【解决方案2】:

André 已经提到了新的at() 函数。在其他情况下,以下可能有用:

template <typename Map, typename K, typename T>
T get(Map &map, const K &key, T def) {
    typename Map::const_iterator it = map.find(key);
    return (it == map.end() ? def : it->second);
}

像这样使用它:

int val = get(intMap, 11, 0);

您也可以有一个const 版本,其优点是返回一个引用(非常量版本返回一个值,因为返回一个默认值的引用是相当有限的):

template <typename Map, typename K, typename T>
const T& get(const Map &map, const K &key, const T &def) {
    typename Map::const_iterator it = map.find(key);
    return (it == map.end() ? def : it->second);
}

只是为了好玩,这段代码很简洁,但为了清晰可能会丢失。它会按照您在下面的评论中所说的:11 不在地图中的情况下:

BOOST_FOREACH(const std::pair<int,int> &newval, intMap.equal_range(11)) {
    val = newval.second;
}

【讨论】:

  • 我想要在不存在密钥的情况下让 val 保持其原始值,就好像我什么都没做一样。
  • @Subway:在这种情况下,您可以使用val = get(intMap, 11, val);(只要val 不是易失性的)。事实上,对于这种情况,非常量版本为默认值采用非常量引用并返回引用是没有问题的,所以如果你的真实值类型是(不像int)复制那么你可以避免任何昂贵的副本,只要类型特殊情况自分配。
【解决方案3】:

我猜这也可以:

int val;
if (intMap.count(11)) val = intMap[11];

简单检查密钥存在...

按照 Karadoc 的建议,在一行中使用 default_value:

int val = (intMap.count(x) ? intMap[x] : default_value);

【讨论】:

  • 请注意,如果值存在,这将涉及两次查找。本身不是问题,但如果性能很重要,记住它会很好。
  • 在这种方法中困扰我的一件事是您正在复制值,而在某些情况下最好访问对值的引用。所以,使用异常,我们可以做类似if( intMap.count(11) == 0 ) throw ...; int&amp; val = intMap[11];
  • 对,但处理异常也可能是个问题,而且 OP 要求提供短代码...
【解决方案4】:

通常,您必须以某种方式检查密钥是否存在。但是,您可以编写一些函数来进行检查并给出值,因此当您调用它时,您将拥有一个单行。当地图中不存在该键时,您需要一些默认值。这是一个快速的实现:

template <class Map, class U>
auto getOrDefault(Map const& m, typename Map::key_type const& key, U&& default_val) 
  -> typename Map::mapped_type
{
  auto pos = m.find(key);
  return pos != m.end() ? pos->second : std::forward<U>(default_val);
}

template <class Map>
auto getOrDefault(Map const& m, typename Map::key_type const& key) 
  -> typename Map::mapped_type
{
  typedef typename Map::mapped_type Value;
  return getOrDefault(m, key, Value{});   
}

//call:
int val = getOrDefault(intMap, 11);       // 0 if key 11 does not exist
int val2 = getOrDefault(intMap, 42, -1);  //-1 if key 42 does not exist

注意:我没有设法给 default_val 一个默认参数 - 也许有人可以帮我解决这个问题。当我尝试使用 U&amp;&amp; default_val = typename Map::mapped_type{} 时,gcc 4.7.2 无法推断出 U

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    相关资源
    最近更新 更多