惯用的 C++ 用于从 const 映射中读取

发布于 2024-07-06 13:46:06 字数 523 浏览 3 评论 0原文

对于 std::map变量,我想这样做:

BOOST_CHECK_EQUAL(variables["a"], "b");

唯一的问题是,在这种情况下variablesconst,所以operator[] 不起作用 :(

现在,有几种解决方法;使用 variables.count("a") 舍弃 const ?variables.find("a")- >第二:std::string() 或者甚至创建一个包装它的函数,在我看来,这些都不像 operator[] 那样好。有一个标准的方法可以做到这一点(漂亮地)吗?

编辑: 只是为了说明你们都不想给出的答案:不,在 C++ 中没有方便、漂亮、标准的方法来做到这一点。我将必须实现支持功能。

For an std::map<std::string, std::string> variables, I'd like to do this:

BOOST_CHECK_EQUAL(variables["a"], "b");

The only problem is, in this context variables is const, so operator[] won't work :(

Now, there are several workarounds to this; casting away the const, using variables.count("a") ? variables.find("a")->second : std::string() or even making a function wrapping that. None of these seem to me to be as nice as operator[]. What should I do? Is there a standard way of doing this (beautifully)?

Edit: Just to state the answer that none of you want to give: No, there is no convenient, beautiful, standard way of doing this in C++. I will have to implement a support function.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

命硬 2024-07-13 13:46:06
template <typename K, typename V>
V get(std::map<K, V> const& map, K const& key)
{
    std::map<K, V>::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : V();
}

根据评论改进实施:

template <typename T>
typename T::mapped_type get(T const& map, typename T::key_type const& key)
{
    typename T::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : typename T::mapped_type();
}
template <typename K, typename V>
V get(std::map<K, V> const& map, K const& key)
{
    std::map<K, V>::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : V();
}

Improved implementation based on comments:

template <typename T>
typename T::mapped_type get(T const& map, typename T::key_type const& key)
{
    typename T::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : typename T::mapped_type();
}
寒江雪… 2024-07-13 13:46:06

抛弃 const 是错误的,因为 map<> 上的operator[] 如果默认构造的字符串不存在,则将创建该条目。 如果映射实际上位于不可变存储中,那么它将失败。 这一定是这样,因为operator[]返回一个非常量引用以允许赋值。 (例如,m[1] = 2)

一个快速的免费函数来实现比较:

template<typename CONT>
bool check_equal(const CONT& m, const typename CONT::key_type& k,
                    const typename CONT::mapped_type& v)
{
    CONT::const_iterator i(m.find(k));
    if (i == m.end()) return false;
    return i->second == v;
}

我会考虑语法糖,如果我想到什么就更新。

...

直接的语法糖涉及一个自由函数,它执行map<>::find()并返回一个包装map<>::const_iterator的特殊类,然后重载了operator==()和operator! =() 允许与映射类型进行比较。 所以你可以这样做:

if (nonmutating_get(m, "key") == "value") { ... }

我不相信这比:

if (check_equal(m, "key", "value")) { ... }

它肯定要复杂得多,而且发生的事情不太明显。

包装迭代器的对象的目的是停止使用默认构造的数据对象。 如果您不在乎,则只需使用“获取”答案即可。

为了回应关于 get 优于比较的评论,希望找到一些未来的用途,我有以下评论:

  • 说出你的意思:调用一个名为“check_equal”的函数可以清楚地表明你正在做一个不创建对象的相等比较。

  • 我建议仅在您有需要时才实现功能。 在此之前做某事通常是一个错误。

  • 根据具体情况,默认构造函数可能会产生副作用。 如果要进行比较,为什么要做额外的事情?

  • SQL 参数:NULL 不等于空字符串。 容器中缺少密钥真的与容器中存在的具有默认构造值的密钥相同吗?

话虽如此,默认构造对象相当于在非常量容器上使用 map<>::operator[]。 也许您当前需要一个返回默认构造对象的 get 函数; 我知道我过去也有过这样的要求。

Casting away const is wrong, because operator[] on map<> will create the entry if it isn't present with a default constructed string. If the map is actually in immutable storage then it will fail. This must be so because operator[] returns a non-const reference to allow assignment. (eg. m[1] = 2)

A quick free function to implement the comparison:

template<typename CONT>
bool check_equal(const CONT& m, const typename CONT::key_type& k,
                    const typename CONT::mapped_type& v)
{
    CONT::const_iterator i(m.find(k));
    if (i == m.end()) return false;
    return i->second == v;
}

I'll think about syntactic sugar and update if I think of something.

...

The immediate syntactic sugar involved a free function that does a map<>::find() and returns a special class that wraps map<>::const_iterator, and then has overloaded operator==() and operator!=() to allow comparison with the mapped type. So you can do something like:

if (nonmutating_get(m, "key") == "value") { ... }

I'm not convinced that is much better than:

if (check_equal(m, "key", "value")) { ... }

And it is certainly much more complex and what is going on is much less obvious.

The purpose of the object wrapping the iterator is to stop having default constructed data objects. If you don't care, then just use the "get" answer.

In response to the comment about the get being preferred over a comparison in the hope of finding some future use, I have these comments:

  • Say what you mean: calling a function called "check_equal" makes it clear that you are doing an equality comparison without object creation.

  • I recommend only implementing functionality once you have a need. Doing something before then is often a mistake.

  • Depending on the situation, a default constructor might have side-effects. If you are comparing, why do anything extra?

  • The SQL argument: NULL is not equivalent to an empty string. Is the absence of a key from your container really the same as the key being present in your container with a default constructed value?

Having said all that, a default constructed object is equivalent to using map<>::operator[] on a non-const container. And perhaps you have a current requirement for a get function that returns a default constructed object; I know I have had that requirement in the past.

浸婚纱 2024-07-13 13:46:06

find 是惯用的形式。 抛弃 const 几乎总是一个坏主意。 您必须保证不执行任何写操作。 虽然这对于地图上的读取访问来说是合理的预期,但规范对此没有任何说明。

如果您知道该值存在,您当然可以放弃使用count进行测试(无论如何,这效率很低,因为这意味着遍历地图两次。即使您不这样做不知道该元素是否存在,我不会使用它,而是使用以下内容:

T const& item(map<TKey, T> const& m, TKey const& key, T const& def = T()) {
    map<TKey, T>::const_iterator i = m.find(key);
    return i == m.end() ? def : i->second;
}

/编辑:正如 Chris 正确指出的那样,T 类型的对象的默认构造可能 成本高昂,特别是因为即使实际上不需要该对象(因为该条目存在)也会这样做。如果是这种情况,请不要使用 def 参数的默认值。在上述情况下。

find is the idiomatic form. Casting away const is almost always a bad idea. You'd have to guarantee that no write operation is performed. While this can be reasonably expected of a read access on a map, the specification doesn't say anything about this.

If you know that the value exists you can of course forego the test using count (which is quite inefficient, anyway, since it means traversing the map twice. Even if you don't know whether the element exists I wouldn't use this. Use the following instead:

T const& item(map<TKey, T> const& m, TKey const& key, T const& def = T()) {
    map<TKey, T>::const_iterator i = m.find(key);
    return i == m.end() ? def : i->second;
}

/EDIT: As Chris has correctly pointed out, default-construction of objects of type T might be expensive, especially since this is done even if this object isn't actually needed (because the entry exists). If this is the case, don't use the default value for the def argument in the above case.

奶气 2024-07-13 13:46:06

有趣的是,有两种方法可以在接受的 get 实现中进行模板类型发现(获取值或返回默认构造对象的方法)。 一,您可以执行已接受的操作:

template <typename K, typename V>
V get1(const std::map<K, V>& theMap, const K const key)
{
    std::map<K, V>::const_iterator iter(theMap.find(key));
    return iter != theMap.end() ? iter->second : V();
}

或者您可以使用映射类型并从中获取类型:

template<typename T>
typename T::mapped_type
get2(const T& theMap, const typename T::key_type& key)
{
    typename T::const_iterator itr = theMap.find(key);
    return itr != theMap.end() ? itr->second : typename T::mapped_type();
}

这样做的优点是传入的密钥类型不会在类型发现中发挥作用,并且可以是可以隐式转换为密钥的东西。 例如:

std::map<std::string, int> data;
get1(data, "hey"); // doesn't compile because the key type is ambiguous
get2(data, "hey"); // just fine, a const char* can be converted to a string

An interesting aside, there are two ways do the template type discovery in the get implementation that was accepted (the one that gets the value or returns a default constructed object). One, you can do what was accepted and have:

template <typename K, typename V>
V get1(const std::map<K, V>& theMap, const K const key)
{
    std::map<K, V>::const_iterator iter(theMap.find(key));
    return iter != theMap.end() ? iter->second : V();
}

or you can use the map type and get the types off of that:

template<typename T>
typename T::mapped_type
get2(const T& theMap, const typename T::key_type& key)
{
    typename T::const_iterator itr = theMap.find(key);
    return itr != theMap.end() ? itr->second : typename T::mapped_type();
}

The advantage of this is that the type of the key being passed in doesn't play in the type discovery and it can be something that can be implicitly converted to a key. For example:

std::map<std::string, int> data;
get1(data, "hey"); // doesn't compile because the key type is ambiguous
get2(data, "hey"); // just fine, a const char* can be converted to a string
烟织青萝梦 2024-07-13 13:46:06

事实上,operator[] 是 std::map 上的非常量运算符,因为如果映射不存在,它会自动在映射中插入键值对。 (哦副作用!)

正确的方法是使用 map::find 并且,如果返回的迭代器有效 (!= map.end()),则返回第二,如您所示。

map<int, int> m;
m[1]=5; m[2]=6; // fill in some couples
...
map<int,int>::const_iterator it = m.find( 3 );
if( it != m.end() ) {
    int value = it->second;
    // ... do stuff with value
}

可以在您正在使用的 std::map 的子类中添加 map::operator[]( const key_type& key ) const ,并将密钥断言为找到,然后返回 it->second

Indeed, operator[] is a non-const one on std::map, since it automatically inserts a key-value pair in the map if it weren't there. (Oooh side-effects!)

The right way is using map::find and, if the returned iterator is valid (!= map.end()), returning the second, as you showed.

map<int, int> m;
m[1]=5; m[2]=6; // fill in some couples
...
map<int,int>::const_iterator it = m.find( 3 );
if( it != m.end() ) {
    int value = it->second;
    // ... do stuff with value
}

You could add a map::operator[]( const key_type& key ) const in a subclass of the std::map you're using, and assert the key to be found, after which you return the it->second.

不念旧人 2024-07-13 13:46:06
std::map<std::string, std::string>::const_iterator it( m.find("a") );
BOOST_CHECK_EQUAL( 
                     ( it == m.end() ? std::string("") : it->second ), 
                     "b" 
                 );

这对我来说看起来不太糟糕......我可能不会为此编写函数。

std::map<std::string, std::string>::const_iterator it( m.find("a") );
BOOST_CHECK_EQUAL( 
                     ( it == m.end() ? std::string("") : it->second ), 
                     "b" 
                 );

That doesn't look too bad to me... I probably wouldn't write a function for this.

趁微风不噪 2024-07-13 13:46:06

遵循 xtofl 专门化地图容器的想法。 下面的方法效果好吗?

template <typename K,typename V>  
struct Dictionary:public std::map<K,V>  
{  
  const V& operator[] (const K& key) const  
  {  
    std::map<K,V>::const_iterator iter(this->find(key));  
    BOOST_VERIFY(iter!=this->end());  
    return iter->second;  
  }  
};  

Following up xtofl's idea of specializing the map container. Will the following work well?

template <typename K,typename V>  
struct Dictionary:public std::map<K,V>  
{  
  const V& operator[] (const K& key) const  
  {  
    std::map<K,V>::const_iterator iter(this->find(key));  
    BOOST_VERIFY(iter!=this->end());  
    return iter->second;  
  }  
};  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文