传递 const 作为此参数仅在使用unordered_map而不是向量时丢弃限定符



我知道对常量对象调用非常量方法会产生错误,如此处所述。这个问题虽然处理相同的错误,但不是重复的,因为它不是关于非常量方法的。

这次我有一个最小的可重现示例:

// 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;

};

getValue2const限定版本只能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_)mapoperator[]执行插入操作,因此在使用operator[]时无法conststd::(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

最新更新