题
对于一个 std::map<std::string, std::string> variables
, 我想要做到这一点:
BOOST_CHECK_EQUAL(variables["a"], "b");
唯一的问题是,在这个环境 variables
是 const
, ,所以 operator[]
不会的工作:(
现在,有几个解决办法;铸造走 const
, 使用 variables.count("a") ? variables.find("a")->second : std::string()
甚至让一个功能包裹。没有这些似乎对我来说不错 operator[]
.我应该怎么做?是否有一个标准的办法,这样做的(美)?
编辑: 只是为了国家的答复,没有你想得到:没有,没有方便的、美丽的、标准的方式这样做在C++。我将要实施一个支持功能。
解决方案
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();
}
其他提示
破const是错误的,因为操作者[]在地图上<>将创造条目,如果这不是本与默认构成串。如果地图实际上是不可改变的储存,然后它就会失败。这必须如此,因为操作者[]返回一个非常量基准,以允许转让。(eg.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;
}
我会考虑的语法糖和更新如果我想的东西。
...
立即法糖参与自由的功能,没有一个地图<>::find()和返回的一个特殊类包装的地图<>::const_iterator,然后有超负载操作员==()以及操作者!=()允许比映类型。所以你可以做一些事情,如:
if (nonmutating_get(m, "key") == "value") { ... }
我不相信这是更好的比:
if (check_equal(m, "key", "value")) { ... }
它肯定是更复杂的和什么是不太显而易见的。
目的对象包裹的迭代是阻止具有默认构成的数据的对象。如果你不照顾,那么就使用的"得到"回答。
在应对评论有关的获得被选在比较希望找到一些未来的使用,我们这些评论:
说你是什么意思:叫一个功能称为"check_equal"很清楚你正在做一个平等比较无需对象的创造。
我建议只执行功能,一旦有需要。做的东西之前,然后往往是一个错误。
根据不同的情况,默认构造可能有的副作用。如果你比较,为什么额外的东西?
SQL参数:NULL不是相当于一个空串。是没有钥匙从你的容器真相同的关键存在你的容器与默认构成的价值?
有说所有的默认构成对象是相当于使用地图<>::员[]在一个非常量的容器中。也许你有一个当前要求得到的功能,返回一个默认构成对象;我知道我必须有这样的要求,在过去的。
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
参数的默认值。
有趣的是,在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
实际上,operator []是std :: map上的非const值,因为它会自动在地图中插入一个键值对(如果它不存在)。 (哦,副作用!)
正确的方法是使用map::find
,如果返回的迭代器有效(!= map.end()
),则返回second
,如图所示。
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
。
std::map<std::string, std::string>::const_iterator it( m.find("a") );
BOOST_CHECK_EQUAL(
( it == m.end() ? std::string("") : it->second ),
"b"
);
对我来说这看起来并不太糟糕......我可能不会为此写一个函数。
跟随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;
}
};