为什么STL映射不使用[]运算符const?


89

为了这个问题,有人为的例子:

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map[x] << std::endl
}

由于[]运算符为非常量,因此无法编译。

不幸的是,因为[]语法看起来很干净。相反,我必须做这样的事情:

void MyClass::MyFunction( int x ) const
{
  MyMap iter = m_map.find(x);
  std::cout << iter->second << std::endl
}

这一直困扰着我。为什么[]运算符是非常量的?


5
operator[]如果给定元素不存在,应该产生什么?
Frerich Raabe

4
@Frerich Raabe:与at成员函数相同:throw std :: out_of_range
Jean-Simon Brochu

Answers:


90

对于std::mapstd::unordered_mapoperator[]如果索引值以前不存在,则将其插入到容器中。这有点不直观,但是事实就是这样。

由于必须允许它失败并插入默认值,因此不能const在容器的实例上使用该运算符。

http://en.cppreference.com/w/cpp/container/map/operator_at


3
std::set没有operator[]
avakar

2
这是正确的答案,但是const版本可以执行与“ at”成员相同的操作。那是抛出一个std :: out_of_range ...
Jean-Simon Brochu

用于读取值时,没有默认值可提供。std::vector的读取运算符[]constmap应该做的一样。
wcochran

51

现在,使用C ++ 11,您可以通过使用at()获得一个更干净的版本

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}

4
如果map有const和non-const at()s-为什么还不一样operator[]?与const版本不插入任何东西,而是抛出?(或在std :: optional时返回可选值,使其成为标准值)
einpoklum 2015年

@einpoklum const正确性的要点主要是静态编译时检查。我宁愿编译器抱怨而不是抛出异常,因为我没有正确使用const对象。
米莉·史密斯

@einpoklum很晚了,但是对于其他读者:具有两个重载来做这样不同的事情将是可怕的。at出现两种变化的唯一原因是因为它执行return *this;,并且重载之间的唯一区别是const返回的引用的-ness。两者at的实际影响是完全相同的(即没有影响)。
HTNW

28

给新读者的注释。
最初的问题是关于STL容器的(不是专门关于std :: map的)

应该注意的是,大多数容器上都有operator []的const版本。
只是std :: map和std :: set没有const版本,这是实现它们的基础结构的结果。

来自std :: vector

reference       operator[](size_type n) 
const_reference operator[](size_type n) const 

同样在第二个示例中,您应该检查找不到元素的失败。

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}

1
std::set根本没有operator[]
大家

2

由于operator []可能会在容器中插入一个新元素,因此它不可能是const成员函数。请注意,operator []的定义非常简单:m [k]等效于(*(((m.insert(value_type(k,data_type())))。first))。second。严格来说,此成员函数是不必要的:它仅是为了方便而存在


0

对于只读容器,索引运算符应仅是const(STL本身实际上并不存在)。

索引运算符不仅用于查看值。


6
问题是,为什么它没有两个重载版本-一个constconst例如,另一个非- std::vector
帕维尔·米纳夫

-2

如果您声明std :: map成员变量是可变的

mutable std::map<...> m_map;

您可以在const成员函数中使用std :: map的非const成员函数。


15
但是,这是一个可怕的想法。
GManNickG

7
如果这样做,您的类的API便会存在。该函数声称它是const(意味着它不会修改任何成员变量),但实际上它可能正在修改m_map数据成员。
Runcible

2
mutable可以用于std::mutex,缓存和调试助手之类的成员。如果将映射用作高速缓存来加速非常昂贵的const“获取”功能,则mutable可以接受。您需要小心,但这并不是一个可怕的主意。
Mark Lakata
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.