【问题标题】:Why isn't the [] operator const for STL maps?为什么 STL 映射的 [] 运算符不是 const?
【发布时间】:2010-12-01 06:52:47
【问题描述】:

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

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
}

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

【问题讨论】:

  • 如果给定元素不存在,operator[] 应该怎么做?
  • @Frerich Raabe:与 at 成员函数相同:抛出 std::out_of_range

标签: c++ constants


【解决方案1】:

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

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

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

【讨论】:

  • std::set 没有operator[]
  • 这是正确的答案,但是 const 版本可以做与“at”成员相同的事情。那就是抛出一个 std::out_of_range...
  • 当用于读取一个值时,没有提供默认值。 std::vector 有一个读操作符[],即constmap 也应该这样做。
  • @Jean-SimonBrochu 所有容器的约定是at 可以抛出而[] 不会。我认为不值得打破这个约定。
【解决方案2】:

现在使用 C++11,您可以使用 at() 获得更简洁的版本

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

【讨论】:

  • 如果map 有常量和非常量at()s - 为什么operator[] 也不一样? const 版本不插入任何东西,而是抛出? (或者返回一个可选的,当 std::optional 使其成为标准时)
  • @einpoklum const 正确性主要是静态编译时检查。我宁愿编译器抱怨也不愿抛出异常,因为我没有正确使用 const 对象。
  • @einpoklum 很晚了,但对于其他读者来说:让两个重载做如此不同的事情会很糟糕。 at 有两种风格的唯一原因是因为它执行 return *this;,而重载之间的唯一区别是返回的引用的 const-ness。两个ats的实际效果是完全一样的(也就是没有效果)。
【解决方案3】:

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

应该注意,大多数容器上都有一个 const 版本的 operator []。
只是 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
    }
}

【讨论】:

  • std::set 根本没有operator[]
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    对于只读容器(在 STL 中并不真正存在),索引运算符只能是 const。

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

    【讨论】:

    • 问题是,为什么它没有两个重载版本-一个const,另一个非const-例如std::vector 确实如此。
    【解决方案6】:

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

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

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

    【讨论】:

    • 不过,这是个糟糕的主意。
    • 如果你这样做,你的类的 API 就存在。该函数声称它是 const ——这意味着它不会修改任何成员变量——但实际上它可能正在修改 m_map 数据成员。
    • mutable 可用于std::mutex、缓存和调试助手等成员。如果地图要用作缓存来加速非常昂贵的const“getter”函数,那么mutable 是可以接受的。您需要小心,但这本身并不是一个糟糕的主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-25
    • 2011-05-09
    • 1970-01-01
    • 2011-06-03
    • 1970-01-01
    • 2010-12-20
    相关资源
    最近更新 更多