我知道对常量对象调用非常量方法会产生错误,如此处所述。这个问题虽然处理相同的错误,但不是重复的,因为它不是关于非常量方法的。
这次我有一个最小的可重现示例:
// Example program
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
class Something
{
public:
int m_value;
Something(): m_value{0} { myVector.push_back(1); myMap["hello"]=3; }
void setValue(int value) { m_value = value; }
int getValue() { return m_value ; }
//int getValue(const int value){ return myVector[value] ; } //<-- this gives an error (just reference)
int getValue(const int value)const { return myVector[value]; }
//int getValue2(const std::string &name) {return myMap[name]; } //<--- this gives an error (just reference)
int getValue2(const std::string &name) const {return myMap[name]; } //HERE this gives an error (this question)
std::vector<int> myVector;
std::unordered_map<std::string,int> myMap;
};
int main()
{
const Something something{}; // calls default constructor
int l= something.getValue(0);
std::cout<<l<<std::endl;
l= something.getValue2("hello"); //<-- HERE the error
std::cout<<l<<std::endl;
return 0;
}
在注释中,有两个方法声明说明了有关非常量方法的观点。我把它们留在那里供参考。这个问题不在他们之上。
您看到返回向量元素的 constgetValue
方法了吗?这没有问题。 现在看到应该返回无序映射元素的 constgetValue2
方法?即使它是一个常量方法,它也会产生错误
In member function 'int Something::getValue2(const string&) const':
17:68: error: passing 'const std::unordered_map<std::basic_string<char>, int>' as 'this' argument of 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = int; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, int> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = int; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]' discards qualifiers [-fpermissive]
我的问题是:为什么只有无序映射在索引时传递常量会生成此错误?
编辑: 感谢非常有用的答案。我将类修改为
// Example program
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
class Something
{
public:
int m_value;
Something(): m_value{0} { myVector.push_back(1); myMap["hello"]=3; }
void setValue(int value) { m_value = value; }
int getValue() { return m_value ; }
//int getValue(const int value){ return myVector[value] ; } //<-- this gives an error
int getValue(const int value)const { return myVector[value]; }
//int getValue2(const std::string &name) {return myMap[name]; } //<--- this gives an error
//this will generate an exception if name is not in the map
int getValue3(const std::string &name) const {
//return myMap[name];
return myMap.at(name);}
int getValue2(const std::string &name) const {
auto iter = myMap.find(name);
return (iter != myMap.end()) ? iter->second : 0;
}
std::vector<int> myVector;
std::unordered_map<std::string,int> myMap;
};
getValue2
的const
限定版本只能const
访问Something
的成员。这意味着它将看到具有const std::unordered_map<std::string,int>
类型的myMap
,并且您不能在myMap
上调用任何非const
成员函数。operator[]
是一个非const
成员函数(它不能被const
,因为它有时必须插入一个值初始化的条目,即当在映射中找不到键时),所以你会收到关于丢弃限定符的错误消息。要解决此问题,您可以使用.at(name)
而不是[name]
.如果在地图中找不到name
,这将引发异常。
something
是一个const
对象,因此getValue(int)
和getValue2(string)
需要const
限定才能在其上调用。 这意味着它们内部的this
指针将始终指向const
对象,因此对对象数据成员的所有操作也需要const
限定。
Something::getValue(int)
调用myVector[value]
,工作正常,因为std::vector::operator[]
具有const
限定的重载,以提供对const std::vector
对象元素的只读访问。
另一方面,Something::getValue2(string)
调用myMap[name]
,这不起作用std::unordered_map::operator[]
因为它根本不是const
限定的,因此不能在const unordered_map
对象上调用它。 如果找不到指定的键,std::(unordered_)map
的operator[]
执行插入操作,因此在使用operator[]
时无法const
std::(unordered_)map
对象。 要在不插入新元素的情况下从const std::(unordered_)map
读取元素,您必须改用映射的find()
方法,该方法const
限定的,例如:
int getValue2(const std::string &name) const {
auto iter = myMap.find(name);
return (iter != myMap.end()) ? iter->second : 0;
}
std::vector
要么在给定索引处具有元素,要么索引越界,在这种情况下,对其operator[]
的调用会调用未定义的行为。
另一方面,仅使用键无法确定std::unordered_map
是否具有该键的值。你必须搜索地图。
若要查找map
是否具有键的值并使用该值,请执行以下操作:
std::unordered_map<int,int> x;
auto it = x.find(3);
if (it == x.end()) {
// element not found
} else {
auto value = it->second;
}
operator[]
做的远不止这些。如果未找到该元素,它将插入具有给定键的默认构造值的元素,并返回对新插入值的引用。它或多或少与:
std::unordered_map<int,int> x;
auto it = x.find(3);
if (it == x.end()) {
it = x.emplace(std::make_pair(3,0).first;
// .second is bool to
// indicate wether an element
// was actually inserted.
// In this case we know it wasn't present before
}
auto value = it->second;
// or simpler:
auto value = x[3];
operator[]
的好处是在任何情况下您都会获得一个有效的元素,但代价是operator[]
无法const
.
TL;博士
应避免越界访问向量。std::vector::operator[]
始终返回现有元素(或调用未定义的行为)。另一方面,在std::unordered_map
中查找不存在的元素很常见。在这种情况下,您需要选择当元素不存在时要执行的操作。一种选择是operator[]
可能会在映射中插入元素,因此无法const
。