我有这个功能:
bool Table::FindCard(const int& i, const Card& card) const{
std::vector<Card>::const_iterator iter = table[i].begin();
for(; iter != table[i].end() ; ++iter){
if(*iter == card){
return true;
}
}
return false;
}
其中Card是一个类,Table是一个类:
std::map<int, std::vector<Card> > table;
如果我运行代码,我会收到此错误:
Table.cc: In member function ‘bool Table::FindCard(const int&, const Card&) const’:
Table.cc:42:50: error: passing ‘const t_map {aka const std::map<int, std::vector<Card> >}’ as ‘this’ argument discards qualifiers [-fpermissive]
std::vector<Card>::const_iterator iter = table[i].begin();
但是,如果我删除const
关键字:
bool Table::FindCard(const int& i, const Card& card){
...
}
一切正常。
原因是因为operator []不是const?我知道const函数只能调用其他const函数。
由于
答案 0 :(得分:3)
原因是因为operator []不是const?我知道const函数只能调用其他const函数。
是;如果密钥不存在,则operator[]
被指定用于创建新的默认构造元素,并且这在const
地图上是不合理的,因此它不是标记为const
。
当然,您可以指定const
版本以引发例外(如at
所做)或 - 我不知道 - 如果密钥不是&#39,请致电terminate
发现,但唉,标准并没有这么说。您必须使用at
或(ugh)find
。
答案 1 :(得分:2)
operator []会将一个元素插入到地图中。它不是const操作。 使用map.at()来保留constness。
答案 2 :(得分:0)
对于std::map
,operator[]
将返回与给定密钥关联的值的引用。如果该密钥不存在,则将插入该密钥。由于这显然会更改地图,因此此功能不是const
,也不能针对const
对象进行调用。
答案 3 :(得分:0)
如上所述:operator[]
是no-cont
成员,您可以使用at
替换。
我只想在 compilable 代码中添加前面的答案,以便为您提供一个直观的示例。
bool Table::FindCard(const int& i, const Card& card) const{
std::vector<Card>::const_iterator iter = table.at(i).begin();
for(; iter != table.at(i).end() ; ++iter){
if(*iter == card){
return true;
}
}
return false;
}
答案 4 :(得分:0)
正如所有c ++库决策一样,这里有一个哲学问题。
在vector
中,operator[]
是针对const和非const情况定义的。这是明智的,因为对于长度为N的向量,{em}总是总是具有含义,无论是否为常数。该元素将存在。
以此为基线,地图应该做什么?没有绝对的标准来确定是否存在任何下标(密钥)。
使用mutable operator[](0...N-1)
,选择默认构造元素是合理的 - 在调用者引用它之后,他希望它存在,并且我们可以使它存在。这是最不让人惊讶的道路。
那么不可变的情况呢?该元素可能存在也可能不存在,如果不存在,我们就不能改变地图,因为这会违反operator[]
的精神。
我们也不应该抛出异常,因为这对于习惯于处理const
的人来说可能会令人惊讶,因为vector
没有预期的异常(或者可能!)。然后,我们会有两个外观相似的界面。
答案是根本不提供可变的运算符[]。开发人员(像你一样)可能会惊讶地发现它不存在,但是他们有机会查看文档并意识到他们正在咆哮错误的树。
如前所述,我们有at()
(如果下标不存在则抛出异常,如向量),我们有find()
。
在boost(很快,c ++ 17)的帮助下,我们可以给自己一个实用功能:
#include <map>
#include <string>
#include <utility>
#include <type_traits>
#include <iostream>
#include <boost/optional.hpp>
template<class Map>
auto maybe_get_impl(Map& map, typename Map::key_type const& key)
{
using reference_type = std::conditional_t<
std::is_const<std::remove_reference_t<Map>>::value,
typename Map::mapped_type const&,
typename Map::mapped_type&>;
boost::optional<reference_type> result;
auto ifind = map.find(key);
if (ifind != map.end())
{
result = ifind->second;
}
return result;
}
template<class K, class V, class Comp, class A>
auto maybe_get(std::map<K, V, Comp, A> const& map, K const& key)
{
return maybe_get_impl(map, key);
}
template<class K, class V, class Comp, class A>
auto maybe_get(std::map<K, V, Comp, A>& map, K const& key)
{
return maybe_get_impl(map, key);
}
int main()
{
std::map<int, std::string> mymap;
mymap.emplace(1, "hello");
mymap.emplace(2, "world");
// note: non-const because we're taking a reference from a mutable map;
std::string part = std::string("goodbye, cruel world");
std::cout << maybe_get(mymap, 1).value_or(part) << std::endl;
std::cout << maybe_get(mymap, 2).value_or(part) << std::endl;
std::cout << maybe_get(mymap, 0).value_or(part) << std::endl;
}
预期产出:
hello
world
goodbye, cruel world