Is it possible to ban modifying loop variables inside the body of for-loops?
在 C 或 C 中,修改 for 循环内的循环变量是令人讨厌的错误的来源:
1 2 3 4 5 6 7 8 9 10 11 12 | int main() { std::vector<int> v (30); std::iota(v.begin(), v.end(), 0); int j = 0; for (size_t i = 0; i < v.size(); i++) { std::cout << v[i] << ' ' << i << '\ '; i++; // oops, I mean j++ } std::cout << j << '\ '; } |
有什么方法可以禁止或警告在编译器或其他东西的帮助下修改循环体内的循环变量?如果可能的话,我该怎么做?
如果你使用 C 的 ranged-for,你可以使循环变量
1 2 3 4 5 6 | for (const size_t i : boost::irange<size_t>(0, v.size())) { std::cout << v[i] << ' ' << i << '\ '; // i++; // error, can't modify const } |
对于 C,你可以创建一个索引类来使用。因此,以下内容将是一个起点。我确信它可以改进,因为我没有考虑太多。
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CIndex { private: size_t m_index; public: CIndex(size_t i=0) : m_index(i) {} ~CIndex() {}; size_t inc(void) { return ++m_index; } size_t val(void) { return m_index; } bool operator < (size_t i) { return m_index < i; } CIndex & operator =(size_t i) = delete; }; |
并且它会被用作:
1 2 3 4 | for (CIndex x; x < 10; x.inc()) { std::cout << argv[x.val()]; x = 3; // generates an error with Visual Studio 2017 } |
您可以使用转换运算符修改上述类,使其更直观并类似于标准
修改后的类看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CIndex { private: size_t m_index; public: CIndex(size_t i = 0) : m_index(i) {} ~CIndex() {}; size_t inc(size_t i = 1) { return (m_index += i); } // increment operator size_t dec(size_t i = 1) { return (m_index -= i); } // decrement operator CIndex & operator =(size_t i) = delete; operator size_t() const { return m_index; } }; |
这将允许您在几乎可以使用
但是对于 C 语言规范,这并没有允许您将变量标记为在特定范围内不可变或不变。
您真正要求的是能够标记特定的代码行以允许更改变量并标记不允许更改变量的其他代码行。 C 语言规范没有该功能。
在 C 中,你可以隐藏名称并重新声明另一个与
1 2 3 4 5 6 7 | for (int i = 0; i < 10; ++i) { const int t = i, i = t; printf("i = %d.\ ", i); // Works. i = 4; // Yields compiler error. } |
我不建议这样做,但您可以通过以下方式使其不那么难看:
1 2 | #define Protect(Type, Original) \\ const Type Auxiliary_##Original = Original, Original = Auxiliary_##Original |
然后使用:
1 2 3 4 5 6 7 | for (int i = 0; i < 10; ++i) { Protect(int, i); printf("i = %d.\ ", i); // Works. i = 4; // Yields compiler error. } |
编辑:回答您的最新评论:
Yes, what I'm looking for was just if there is a compiler option that warns it. If there isn't, maybe I should just code more carefully.
不,不幸的是,在 C 中没有。是的,您应该更仔细地编码。一般来说,我建议您考虑一下为什么要拥有这样的功能。如果循环内的"保护"索引变量是一个问题,我会首先问自己我的编码风格是否有意义并且是一致的。
正如 Eric Postpischil 所注意到的,您可以使用一个临时变量将您的索引变量隐藏在一个内部块中作为
然而,这会产生阴影警告(特别是
1 2 3 4 5 | shadow.c:9:14: warning: declaration of ‘i’ shadows a previous local [-Wshadow] const int i = t; ^ shadow.c:4:11: note: shadowed declaration is here for (int i = 0; i < 10; ++i) |
为避免这种情况,您可以使用简单的诊断
1 2 3 4 5 6 7 8 9 10 11 12 13 | for (int i = 0; i < 10; ++i) { const int t = i; #pragma GCC diagnostic push #pragma GCC diagnostic ignored"-Wshadow" { const int i = t; printf("i = %d\ ", i); i = 4; // Will yield a compiler error. } #pragma GCC diagnostic pop } |
上面的代码有效(当然删除了
注意:这肯定不是好的做法,但 AFAICT 它是在 C 中实现这种行为的唯一方法。