包装地图访问,聪明还是愚蠢

wrapping map access, smart or stupid?

本文关键字:地图 访问 包装      更新时间:2023-10-16

我正在访问一段非性能关键代码中的许多映射。我不想写通常的发现/=结束所有的时间来检查丢失的钥匙。我也不想盲目使用[]并获取默认值。下面的包装器函数是聪明的还是愚蠢的?有没有更简单的方法?有没有我没有考虑的副作用?

template<typename M>
static typename M::mapped_type getMapValue(const M& m, typename M::key_type key) {
    typename M::const_iterator it = m.find(key);
    if (it != m.end()) {
        return it->second;
    } else {
        std::cerr << "Key: " << key << " not found!" << std::endl;
        std::cerr << "Returning default value." << std::endl;
        return typename M::mapped_type();
    }
}

我建议使用std::map::at,然后客户端代码必须决定如何处理从具有不在映射中的键的调用引发的任何std::out_of_range异常。

std::map<std::string, int> m;
....
int n = m.at("hello!");

智能

封装std容器通常是个好主意,因为如果您愿意,它可以让您在将来交换它们。

然而,返回默认值是正确的行为吗?这可能是你的情况,但在大多数情况下,它不会是。

每当STL接口对于某些程序逻辑变得繁琐时,我倾向于包装整个容器,而不仅仅是访问单个函数。通常,我会从现有的容器类型中私下派生,并再次公开我需要的部分。此外,我添加了我需要的实际接口,例如运算符[],用于const-map之类的对象:

struct mycontainer:
    private map<string, string>
{
    typedef map<string, string> base;
    using base::iterator;
    using base::const_iterator;
    using base::begin;
    using base::end;
    string const& operator[](string const& k) const
    {
        const_iterator it = find(k);
        if(it == end())
            throw runtime_error("lookup failure");
        return it->second;
    }
};

注:

  • 如果您需要特殊的迭代器,Boost中有一个帮助程序库,可以轻松创建自定义迭代器类型
  • 您还可以显著更改界面,例如,返回默认值而不将其添加到映射中,或返回boost::可选。后者是IMHO,这是一种的方法,可以在一步中轻松检查是否存在并检索值

如果这个想法是检查的,那么你可能想要这样的东西:

if (theMap.count(theKey)) {
  doSomethingWith(theMap[theKey]);
} else {
  doSomethingDefault();
}

名称计数非常尴尬,因为如果元素存在,它只返回true,如果不存在,则返回false。我认为它应该与一些支持每个键多个值的容器保持一致,但后来他们从未编写过那个容器。仅仅将"count"重命名为"contains"可能不值得包装映射。

我认为当找不到元素时,最好抛出一个异常。谁来监视你的程序,不管怎样,它会打印出所有这些警告?

此外,考虑通过const引用获取密钥,因为std::map会这样做。