为什么下标操作符在c++中经常成对出现

why do subscript operators C++ often comes in pair?

本文关键字:常成 下标 操作符 c++ 为什么      更新时间:2023-10-16

c++ FAQ定义了一个模板容器Matrix,以避免棘手的new delete代码。教程中说下标操作符经常成对出现?为什么呢?

T&       operator() (unsigned i, unsigned j); 
T const& operator() (unsigned i, unsigned j) const;

为什么呢?

这也称为:const重载。

FAQ给出了线索。你还有什么意见吗?

特别是,mutate()是否应该遵守仅在const对象上安全使用的某些规则?

简单来说,因为赋值操作有两个面:左边和右边。

非const版本:T& operator() (unsigned i, unsigned j);主要用于赋值操作的左侧(即用作赋值操作的目标)。

const版本:T const& operator() (unsigned i, unsigned j) const;专门用于赋值操作的右侧。

请注意这里的措辞差异:const版本只能在赋值语句的右侧使用,而非const版本可以在赋值语句的两侧使用。但是,如果您有一个const限定的对象,则只能调用const限定的成员函数,因此在这种情况下根本不能使用它。这正是你(至少通常)想要的——它可以防止修改你说不应该修改的对象(通过const限定它)。

mutate而言,它通常仅用于在逻辑状态和状态之间存在一些差异的对象。一个常见的例子是惰性地执行某些(通常是昂贵的)计算的类。为了避免重新计算该值,它在计算完后保存该值:

class numbers { 
    std::vector<double> values;
    mutable double expensive_value;
    bool valid;
public:
    numbers() : valid(false) {}
    double expensive_computation() const { 
        if (valid) return expensive_value;
        // compute expensive_value here, and set `valid` to true
    }
};

所以在这里,expensive_computation的结果完全取决于values的值。如果您不关心速度,您可以在每次用户调用expensive_computation时重新计算该值。在const对象上重复调用它总是会产生相同的结果——所以调用了一次之后,我们假设它可能会被再次调用,为了避免重复执行同样昂贵的计算,我们只是将值保存到expensive_value中。然后,如果用户再次请求,我们只返回该值。

换句话说,从逻辑的角度来看,即使修改了expensive_value,对象仍然是const。对象的可见状态不会改变。我们所做的只是允许它更快地完成const操作。

为了使其正常工作,我们还需要在用户修改values的内容时将valid设置回false。例如:

void numbers::add_value(double new_val) {
   values.push_back(new_val);
   valid = false;
}

在某些情况下,我们可能还需要一个中间级别的有效性——我们可能能够更快地重新计算expensive_value,例如,通过确切地知道哪些数字被添加到values,而不仅仅是用一个布尔值来表示它当前是否有效。

我应该补充说c++ 11增加了一些关于constmutable的新要求。长话短说,在大多数情况下,您需要确保const和/或mutable的任何内容也是线程安全的。你可能想看看Herb Sutter的视频。然而,我确实觉得有必要补充一点,我认为他关于mutable的结论可能有点夸张(但我宁愿你自己看并决定,而不是相信我的话)。

T&对象上调用下标操作符,第一个非const操作符被调用。然后,允许执行inspect和mutate操作。

T const &对象上调用下标操作符,调用第二个const操作符。然后,允许inspect,但不允许mutate。

示例在这里

void f(MyFredList const& a)  ← the MyFredList is const
{
  // Okay to call methods that DON'T change the Fred at a[3]:
  Fred x = a[3];
  a[3].inspect();
  // Error (fortunately!) if you try to change the Fred at a[3]:
  Fred y;
  a[3] = y;       ← Fortunately(!) the compiler catches this error at compile-time
  a[3].mutate();  ← Fortunately(!) the compiler catches this error at compile-time
}

归功于c++ FAQ,更多内容在这里。