有没有办法将这个 "for i" c++ 预处理器宏转换为现代 C++ (11+)?
Is there a way to convert this "for i" c++ preprocessor macro to modern C++ (11+)?
我正在寻找一种方法来用更现代的东西替换这个 C++ 预处理器宏。
#define fori(FORI_TYPE, FORI_FROM, FORI_TO) \
for(FORI_TYPE i{FORI_FROM}; \
((FORI_FROM) < (FORI_TO)) ? (i < (FORI_TO)) : (i > (FORI_TO)); \
((FORI_FROM) < (FORI_TO)) ? ++i : --i )
理想情况下,我可以摆脱所有的 ?运算符(constexpr 在这里有用吗?)并且 "fori" 不会像现在使用 proceprocessor 版本(对 ? 运算符的评估)那样产生任何间接费用。另外,类型安全。
用法示例:
fori(size_t, 0, n)
{
cout << i << endl;
}
就其价值而言,您总是需要知道迭代的方向,因此您无法摆脱这种开销。也就是说,通过远离宏,您至少可以使其更容易优化(部分通过使输入 const
促进对它们的 duplicate/similar 条件的折叠,部分通过预先计算 "step" 完全消除其中一些条件的距离)。
就宏而言,那个还不错(尽管它可能会使用一个 ()
或两个……)。
真正 "modern" 的做法是使用 counting iterator, or something related to irange
。
例如,天真地改编 Neil 的代码以提供自动步进方向检测:
#include <iostream>
template <class T>
class range
{
private:
class iter
{
private:
T at, step;
public:
iter(T at, T step) : at(at), step(step) {}
bool operator!=(iter const& other) const { return at != other.at; }
T const& operator*() const { return at; }
iter& operator++() { at += step; return *this; }
};
T begin_val;
T end_val;
T step_val;
public:
range(const T begin_val, const T end_val)
: begin_val(begin_val)
, end_val(end_val)
, step_val(begin_val > end_val ? -1 : 1)
{}
iter begin() { return iter(begin_val, step_val); }
iter end() { return iter(end_val, step_val); }
};
int main()
{
for (auto i : range<unsigned>(42, 10))
std::cout << i << ' ';
std::cout << '\n';
}
// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11
(live demo)
或者,坦率地说,您可以编写以下内容并完成它:
#include <iostream>
int main()
{
for (unsigned int i = 42; i > 10; --i)
std::cout << i << ' ';
std::cout << '\n';
}
// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11
(live demo)
每个人都会明白这一点;无需技巧。
在任何一种情况下,尽管有我上面的示例,I'd actually advise not using unsigned types for this.
我们可以通过使用模板来摆脱宏,尽管这确实意味着必须添加回调函数。
//f is of the form void func(int i)
template <typename T, typename F>
void fori(const T from, const T to, F f)
{
for(
T i = from;
(from < to) ? (i < to) : (i > to);
(form < to) ? ++i : --i
) {
f(i);
}
用法:
fori<int>(1, 20, [](int i) {
std::cout << i << std::endl;
});
这也可以使用 C++20 概念进行改进。
要生成一个类型的序列,我们需要使用生成器。范围将使用它的输出:
for(auto i: range<size_t>(0, n)) {
std::cout << n << " ";
}
并且:
template <typename T> class range {
T a, b;
public:
typename wrap<T> begin() const { return a; }
typename wrap<T> end() const { return b; }
};
包装器会将迭代器应该携带的一些属性(类型和方法)赋予值,但对于数字类型来说相当简单。
我正在寻找一种方法来用更现代的东西替换这个 C++ 预处理器宏。
#define fori(FORI_TYPE, FORI_FROM, FORI_TO) \
for(FORI_TYPE i{FORI_FROM}; \
((FORI_FROM) < (FORI_TO)) ? (i < (FORI_TO)) : (i > (FORI_TO)); \
((FORI_FROM) < (FORI_TO)) ? ++i : --i )
理想情况下,我可以摆脱所有的 ?运算符(constexpr 在这里有用吗?)并且 "fori" 不会像现在使用 proceprocessor 版本(对 ? 运算符的评估)那样产生任何间接费用。另外,类型安全。
用法示例:
fori(size_t, 0, n)
{
cout << i << endl;
}
就其价值而言,您总是需要知道迭代的方向,因此您无法摆脱这种开销。也就是说,通过远离宏,您至少可以使其更容易优化(部分通过使输入 const
促进对它们的 duplicate/similar 条件的折叠,部分通过预先计算 "step" 完全消除其中一些条件的距离)。
就宏而言,那个还不错(尽管它可能会使用一个 ()
或两个……)。
真正 "modern" 的做法是使用 counting iterator, or something related to irange
。
例如,天真地改编 Neil 的代码以提供自动步进方向检测:
#include <iostream>
template <class T>
class range
{
private:
class iter
{
private:
T at, step;
public:
iter(T at, T step) : at(at), step(step) {}
bool operator!=(iter const& other) const { return at != other.at; }
T const& operator*() const { return at; }
iter& operator++() { at += step; return *this; }
};
T begin_val;
T end_val;
T step_val;
public:
range(const T begin_val, const T end_val)
: begin_val(begin_val)
, end_val(end_val)
, step_val(begin_val > end_val ? -1 : 1)
{}
iter begin() { return iter(begin_val, step_val); }
iter end() { return iter(end_val, step_val); }
};
int main()
{
for (auto i : range<unsigned>(42, 10))
std::cout << i << ' ';
std::cout << '\n';
}
// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11
(live demo)
或者,坦率地说,您可以编写以下内容并完成它:
#include <iostream>
int main()
{
for (unsigned int i = 42; i > 10; --i)
std::cout << i << ' ';
std::cout << '\n';
}
// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11
(live demo)
每个人都会明白这一点;无需技巧。
在任何一种情况下,尽管有我上面的示例,I'd actually advise not using unsigned types for this.
我们可以通过使用模板来摆脱宏,尽管这确实意味着必须添加回调函数。
//f is of the form void func(int i)
template <typename T, typename F>
void fori(const T from, const T to, F f)
{
for(
T i = from;
(from < to) ? (i < to) : (i > to);
(form < to) ? ++i : --i
) {
f(i);
}
用法:
fori<int>(1, 20, [](int i) {
std::cout << i << std::endl;
});
这也可以使用 C++20 概念进行改进。
要生成一个类型的序列,我们需要使用生成器。范围将使用它的输出:
for(auto i: range<size_t>(0, n)) {
std::cout << n << " ";
}
并且:
template <typename T> class range {
T a, b;
public:
typename wrap<T> begin() const { return a; }
typename wrap<T> end() const { return b; }
};
包装器会将迭代器应该携带的一些属性(类型和方法)赋予值,但对于数字类型来说相当简单。