jho*_*ack 83 c++ algorithm variables for-loop constants
考虑一个标准 for 循环:
for (int i = 0; i < 10; ++i)
{
// do something with i
}
Run Code Online (Sandbox Code Playgroud)
我想防止变量i在for循环体中被修改。
但是,我不能声明i,const因为这会使增量语句无效。有没有办法在增量语句之外创建i一个const变量?
cig*_*ien 119
从 c++20 开始,您可以像这样使用range::views::iota:
for (int const i : std::views::iota(0, 10))
{
std::cout << i << " "; // ok
i = 42; // error
}
Run Code Online (Sandbox Code Playgroud)
这是一个演示。
从 c++11 开始,您还可以使用以下技术,该技术使用 IIILE(立即调用的内联 lambda 表达式):
int x = 0;
for (int i = 0; i < 10; ++i) [&,i] {
std::cout << i << " "; // ok, i is readable
i = 42; // error, i is captured by non-mutable copy
x++; // ok, x is captured by mutable reference
}(); // IIILE
Run Code Online (Sandbox Code Playgroud)
这是一个演示。
请注意,这[&,i]意味着i由非可变副本捕获,其他所有内容均由可变引用捕获。该();在循环的结束只是意味着该拉姆达立即调用。
Hum*_*ler 45
对于喜欢 Cigien 的std::views::iota答案但不在 C++20 或更高版本中工作的任何人来说,实现std::views::iota兼容c++11或更高版本的简化和轻量级版本是相当简单的。
它只需要:
operator++和 的东西operator*)包装一个整数值(例如 an int)begin()并end()返回上述迭代器的“范围”类类。这将允许它在基于范围的for循环中工作一个简化的版本可能是:
#include <iterator>
// This is just a class that wraps an 'int' in an iterator abstraction
// Comparisons compare the underlying value, and 'operator++' just
// increments the underlying int
class counting_iterator
{
public:
// basic iterator boilerplate
using iterator_category = std::input_iterator_tag;
using value_type = int;
using reference = int;
using pointer = int*;
using difference_type = std::ptrdiff_t;
// Constructor / assignment
constexpr explicit counting_iterator(int x) : m_value{x}{}
constexpr counting_iterator(const counting_iterator&) = default;
constexpr counting_iterator& operator=(const counting_iterator&) = default;
// "Dereference" (just returns the underlying value)
constexpr reference operator*() const { return m_value; }
constexpr pointer operator->() const { return &m_value; }
// Advancing iterator (just increments the value)
constexpr counting_iterator& operator++() {
m_value++;
return (*this);
}
constexpr counting_iterator operator++(int) {
const auto copy = (*this);
++(*this);
return copy;
}
// Comparison
constexpr bool operator==(const counting_iterator& other) const noexcept {
return m_value == other.m_value;
}
constexpr bool operator!=(const counting_iterator& other) const noexcept {
return m_value != other.m_value;
}
private:
int m_value;
};
// Just a holder type that defines 'begin' and 'end' for
// range-based iteration. This holds the first and last element
// (start and end of the range)
// The begin iterator is made from the first value, and the
// end iterator is made from the second value.
struct iota_range
{
int first;
int last;
constexpr counting_iterator begin() const { return counting_iterator{first}; }
constexpr counting_iterator end() const { return counting_iterator{last}; }
};
// A simple helper function to return the range
// This function isn't strictly necessary, you could just construct
// the 'iota_range' directly
constexpr iota_range iota(int first, int last)
{
return iota_range{first, last};
}
Run Code Online (Sandbox Code Playgroud)
我已经在上面定义了constexpr它支持的地方,但是对于像 C++11/14 这样的早期 C++ 版本,您可能需要删除constexpr在这些版本中不合法的地方。
上面的样板使以下代码能够在 C++20 之前的版本中工作:
for (int const i : iota(0, 10))
{
std::cout << i << " "; // ok
i = 42; // error
}
Run Code Online (Sandbox Code Playgroud)
优化时将生成与C++20std::views::iota解决方案和经典for-loop 解决方案相同的程序集。
这适用于任何符合 C++11 的编译器(例如像 的编译器gcc-4.9.4),并且仍然生成与基本for-loop 对应物几乎相同的程序集。
注意:该iota辅助函数仅仅是特征奇偶与C ++ 20std::views::iota溶液; 但实际上,您也可以直接构造一个iota_range{...}而不是调用iota(...). 如果用户希望将来切换到 C++20,前者只是提供了一个简单的升级路径。
Art*_*ius 29
亲吻版本...
for (int _i = 0; _i < 10; ++_i) {
const int i = _i;
// use i here
}
Run Code Online (Sandbox Code Playgroud)
如果您的用例只是为了防止意外修改循环索引,那么这应该会使这样的错误显而易见。(如果你想防止故意修改,好吧,祝你好运......)
Al *_* rl 13
您不能在接受 i 作为常量的函数中移动 for 循环的部分或全部内容吗?
它不如提出的某些解决方案最优,但如果可能的话,这很容易做到。
编辑:只是一个例子,因为我往往不清楚。
for (int i = 0; i < 10; ++i)
{
looper( i );
}
void looper ( const int v )
{
// do your thing here
}
Run Code Online (Sandbox Code Playgroud)
JeJ*_*eJo 12
如果您无权访问c++20,则使用函数进行典型改造
#include <vector>
#include <numeric> // std::iota
std::vector<int> makeRange(const int start, const int end) noexcept
{
std::vector<int> vecRange(end - start);
std::iota(vecRange.begin(), vecRange.end(), start);
return vecRange;
}
Run Code Online (Sandbox Code Playgroud)
现在你可以
for (const int i : makeRange(0, 10))
{
std::cout << i << " "; // ok
//i = 100; // error
}
Run Code Online (Sandbox Code Playgroud)
(见演示)
更新:受@Human-Compiler评论的启发,我想知道给出的答案在性能情况下是否有任何差异。事实证明,除了这种方法之外,所有其他方法都令人惊讶地具有相同的性能(对于范围[0, 10))。这种std::vector方法是最糟糕的。
(见在线快速工作台)
Vla*_*ein 10
这是一个 C++11 版本:
for (int const i : {0,1,2,3,4,5,6,7,8,9,10})
{
std::cout << i << " ";
// i = 42; // error
}
Run Code Online (Sandbox Code Playgroud)
#include <cstdio>
#define protect(var) \
auto &var ## _ref = var; \
const auto &var = var ## _ref
int main()
{
for (int i = 0; i < 10; ++i)
{
{
protect(i);
// do something with i
//
printf("%d\n", i);
i = 42; // error!! remove this and it compiles.
}
}
}
Run Code Online (Sandbox Code Playgroud)
注意:我们需要嵌套作用域,因为语言中有一个惊人的愚蠢:在for(...)头文件中声明的变量被认为与在{...}复合语句中声明的变量处于同一嵌套级别。这意味着,例如:
for (int i = ...)
{
int i = 42; // error: i redeclared in same scope
}
Run Code Online (Sandbox Code Playgroud)
什么?我们不是刚打开一个花括号吗?此外,它是不一致的:
void fun(int i)
{
int i = 42; // OK
}
Run Code Online (Sandbox Code Playgroud)