如何安全地从STL map或unordered_map中获取一个值?

huangapple go评论56阅读模式
英文:

How can I safe get a value from STL map or unordered_map?

问题

以下是您要翻译的内容:

如何从STL map或unordered_map中安全获取键的值?
明显的答案是

const auto it = map.find(key);
if (it != std::end(map))
{ 
  myValue = get<1>(*it);
}
else
{
  // no key case ...
}

由于这段代码经常被使用,为什么STL没有类似于C#的TryGetValue的辅助方法呢?STL已经包含了许多辅助方法,例如std::get<>()std::make_...<>()std::begin\end()等等。因此,std::try_get()将遵循这一逻辑。

类似于以下内容:

namespace std
{
    template<class Key, class T, class Hash = hash<Key>, class KeyEqual = equal_to<Key>, class Allocator = allocator< pair<const Key, T>>>
    bool try_get(const unordered_map<Key, T, Hash, KeyEqual, Allocator>& map, const Key& key, T* value)
    {
        const auto it = map.find(key);
        const bool res = it != end(map);
        if (res && value != nullptr)
        {
            *value = get<1>(*it);
        }

        return res;
    }

    template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>>
    bool try_get(const map<Key, T, Compare, Allocator>& map, const Key& key, T* value)
    {
        const auto it = map.find(key);
        const bool res = it != end(map);
        if (res && value != nullptr)
        {
            *value = get<1>(*it);
        }

        return res;
    }
}

示例:
这个方法在以下情况下会很有用,当你有一组预定义值的映射,但默认值也适用于你时:

std::map<TKey, TValue> settings = /* 反序列化 */;

...

TValue myValue = /* 默认值 */ ;
std::try_get(settings, key, &myValue);

// 在下面的代码中使用myValue,它包含默认值或来自设置的预定义值

另一个示例,你有几个缓存的已初始化对象,当缓存不包含与键关联的对象时,你需要使用其中任何一个,或者在没有找到时回退到默认实现。


std::map<TKey, TObjKeeper> generalDependecyObjects = /* 初始化 */;
std::map<TKey, TObjKeeper> specificDependecyObjects = /* 初始化 */;

TObjKeeper obj;
if (!std::try_get(generalDependecyObjects, objectId, &obj) &&
    !std::try_get(specificDependecyObjects, objectId, &obj))
{
  obj = /*回退到默认实现*/;
}
英文:

How can I safe get the value by key from STL map or unordered_map?
The obvious answer is

const auto it = map.find(key);
if (it != std::end(map))
{ 
  myValue = get<1>(*it);
}
else
{
  // no key case ...
}

By this code is used so often, why does not STL contain helper methods something similar to TryGetValue from C# ? STL already contains a lot of helpers, e.g. std::get<>(), std::make_...<>(), std::begin\end() and so on. So std::try_get() would follow this logic.

Something similar to:

namespace std
{
    template<class Key, class T, class Hash = hash<Key>, class KeyEqual = equal_to<Key>, class Allocator = allocator< pair<const Key, T>>>
    bool try_get(const unordered_map<Key, T, Hash, KeyEqual, Allocator>& map, const Key& key, T* value)
    {
        const auto it = map.find(key);
        const bool res = it != end(map);
        if (res && value != nullptr)
        {
            *value = get<1>(*it);
        }

        return res;
    }

    template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>>
    bool try_get(const map<Key, T, Compare, Allocator>& map, const Key& key, T* value)
    {
        const auto it = map.find(key);
        const bool res = it != end(map);
        if (res && value != nullptr)
        {
            *value = get<1>(*it);
        }

        return res;
    }
}

Examples:
This method would be helpful in case when you have a map of predefined values but default value is also suits you:

std::map<TKey, TValue> settings = /* deserialization */;

...

TValue myValue = /* default value */ ;
std::try_get(settings, key, &myValue);

// use myValue in following code, 
// it contains ether default or predefined value from settings

Another example, you have several caches with initialized objects and you need to use any of them that correspond key or fallback to default implementation when caches do not contain key associated object.


std::map<TKey, TObjKeeper> generalDependecyObjects = /* initialization */;
std::map<TKey, TObjKeeper> specificDependecyObjects = /* initialization */;

TObjKeeper obj;
if (!std::try_get(generalDependecyObjects, objectId, &obj) &&
    !std::try_get(specificDependecyObjects, objectId, &obj))
{
  obj = /*fallback to default implementation*/;
}

答案1

得分: 1

首先,你的 try_get 函数没有处理 else 部分,因此你仍然需要编写一个 if-else 来检查从 find 返回的迭代器,而是检查从 try_get 返回的 bool 值。

此外,你需要考虑在 if 语句中允许有条件的初始化语句。因此,你更应该比较如下:

if (auto it = map.find(key); it != std::end(map))
{ 
  myValue = get<1>(*it);
}
else
{
  // 无键情况...
}

现在我不太确定你是否能说服其他人以下的方式更好:

DataType value;  // 必须是可默认构造的类型!
if (try_get(map, &value))
{ 
  myValue = get<1>(value);
}
else
{
  // 无键情况...
}
英文:

First, your try_get does not handle the else, so you would still have to write an if-else, not to check the iterator returned from find but to check the bool returned from try_get.

Moreover you need to consider that there is a conditional init-statment allowed in if statements. Hence you rather need to compare against:

if (auto it = map.find(key); it!=std::end(map))
{ 
  myValue = get&lt;1&gt;(*it);
}
else
{
  // no key case ...
}

And now I am not sure anymore if you can convince others that the following would be so much better:

DataType value;  // must be default constructible!
if (try_get(map,&amp;value))
{ 
  myValue = get&lt;1&gt;(value);
}
else
{
  // no key case ...
}

huangapple
  • 本文由 发表于 2023年6月13日 16:41:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76463126.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定