for循环边界重新计算每次迭代?

时间:2015-05-14 19:41:09

标签: c++ c++11 for-loop optimization

假设您有一个类似的类:

class A {
public:
    A(const size_t m, const size_t n) :
        m_(m), n_(n), data_(m*n ? new double[m*n] : nullptr) {}
    ~A() { delete[] data_; }

    void foo() const {
        // m_*n_ evaluated each iteration?
        for (size_t i = 0; i<m_*n_; ++i)
            data_[i] = 0.0;

        // this probably wont but what about the above?
        for (auto& i : *this)
            i = 0.0;
    }

    double* begin() const { return data_; }
    double* end() const { return data_ + m_*n_; }

private:
    size_t m_, n_;
    double* data_;
};

由于foo是const和m_,​​n_是成员变量,编译器应该知道这些不能改变。是每次评估产品还是编译器优化它?

1 个答案:

答案 0 :(得分:0)

问题1:

   // m_*n_ evaluated each iteration?
    for (size_t i = 0; i<m_*n_; ++i)
        data_[i] = 0.0;

答案:可能,但你永远不知道。编译器变得非常聪明。

通过将m_n_声明为const成员,您可以大大提高赔率。无论如何你应该这样做,恕我直言,因为那个阶级的逻辑需要它;如果m_n_发生变化,则需要重新分配data_。将变量声明为const告诉编译器即使它看不到类的所有成员函数的所有定义,也可以假定m_n_不要改变。

问题2:

    for (auto& i : *this)
        i = 0.0;

在这种情况下,根据定义,的结束值将被重新计算。这就是定义语句范围的方式。

一般来说,之间存在很大差异:

 for (auto p = container.begin(), lim = container.end();
      p != lim;
      ++p) { ... }

 for (auto p = container.begin();
      p != container.end();
      ++p) { ... }

在第一种情况下,假设在循环期间不修改容器,因为修改将使迭代器无效(例如lim迭代器)。 (有些容器类型不适用。但对于大多数容器类型,在循环期间应避免修改。)

在第二种情况下,每次都会计算限制值,因此如果修改了容器,则没有问题。至少,最终测试没有问题。您仍然需要确保p不能被无效,对于某些容器类型 - 例如,无序的映射和集合 - 元素序列可以通过修改来更改,因此添加新元素可能会导致一些迭代中缺少的元素。

语法for ( variable : expression)的范围被明确定义为转换为上述第一种迭代样式,其中限制在迭代开始时计算一次,因此可能使限制迭代器结果无效的突变在未定义的行为。

在大多数情况下,您应该避免此类突变,并且应该使用范围语法或等效样式。但是有些应用程序每次都重新计算限制是合适的。这样一个用例的最好例子可能就是工作清单,可以用std::deque建模:

for (auto work = workqueue.begin();
     work != workqueue.end();
     ++work) {
  /* ... */
  if (some_condition) {
    work_queue.emplace_back(work_item);
  }
  /* ... */
}

例如,这是图算法中的常见风格。