【问题标题】:why do subscript operators C++ often comes in pair?为什么下标运算符 C++ 经常成对出现?
【发布时间】:2013-03-04 08:44:32
【问题描述】:

C++ FAQ 正在定义一个模板容器Matrix 以避免棘手的new delete 代码。教程说下标运算符经常成对出现?为什么会这样?

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

为什么会这样?

这也称为:常量重载。

常见问题解答提供了线索。你有额外的 cmets 吗?

特别是,mutate() 是否应该遵守某些规则才能安全地仅用于 const 对象?

【问题讨论】:

  • Operator overloading的可能重复
  • 公平地说,这里只提到operator[](类似数组的类)。

标签: c++ operator-overloading constants operator-keyword


【解决方案1】:

简单地说,因为赋值有两个方面:左侧和右侧。

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

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

请注意这里的措辞差异:const 版本只能用于赋值的右侧,而非 const 版本可以用于任何一边。然而,如果你有一个const-qualified 对象,你只能调用 const-qualified 成员函数,所以在这种情况下它根本不能使用。这正是您(至少通常)想要的——它可以防止修改您说过不应该修改的对象(通过 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 对象上重复调用它总是会产生相同的结果——所以调用它一次后,我们假设它可能会被再次调用,并且为了避免重复执行相同的昂贵计算,我们只需将值保存到 @987654330 @。然后,如果用户再次请求它,我们只需返回该值。

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

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

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

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

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

【讨论】:

  • 非常感谢! “但是,如果你有一个 const 限定的对象,你只能调用 const 限定的成员函数,所以在这种情况下它根本不能使用。”,你的意思是非 const 版本根本不能使用?
  • "As far as mutategoes..." - 你展示了一个可以在非常量成员上调用 const 的情况(使用mutable),并用一个惰性计算示例进行说明逻辑上的 const 成员可以看到它的值变化,对吗?我问是因为例如解释非常清晰和完整,但我错过了它为什么会出现在那里。
【解决方案2】:

T&amp; 对象上调用下标运算符,调用第一个非常量运算符。然后,允许进行检查和变异操作。

T const &amp; 对象上调用下标运算符,调用第二个常量运算符。然后,允许检查,但不允许变异。

例子在这里

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++ 常见问题解答,更多 here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    相关资源
    最近更新 更多