我在代码中经常遇到情况,我希望根据运行时条件以直接或相反的顺序迭代范围。这通常会产生以下代码
if (reverse) {
using boost::adaptors::reversed;
for (auto const & x : range | reversed) do_stuff(x);
} else {
for (auto const & x : range) do_stuff(x);
}
或
std::vector<Foo> v(range.begin(), range.end());
if (reverse) boost::range::reverse(v);
for (auto const & x : v) do_stuff(x);
包含代码重复(第一个)或效率低下(第二个)。
我已经考虑了一个假设的增强范围适配器,该适配器将有条件地扭转范围,以便我可以写
using boost::adaptors::reversed_if;
for (auto const & x : range | reversed_if(reverse)) do_stuff(x);
我可以自己实施(从这里开始),但我不确定如何进行。为了支持运行时条件,恐怕我必须在每次迭代中检查一个布尔值,以确定迭代方向或使用虚拟性来派遣迭代代码。这是在Boost范围适配器中未提供的原因吗?
任何替代解决方案?
如果要避免以每种方式检查运行时检查,则必须将运行时值转换为循环结构之外的编译时值。
在这种情况下,我们希望范围我们循环逐渐变化,而身体则不变。
简单做到这一点的方法是为身体编写一个lambda,然后有一个开关以选择哪个循环。
auto do_stuff = [&](auto&& elem){ /* code */ };
if (reverse) {
using boost::adaptors::reversed;
for (auto const & x : range | reversed) do_stuff(x);
} else {
for (auto const & x : range) do_stuff(x);
}
我们已经在循环外完成了运行时调度
我们可以做一个这样的适配器:
magic_switch
( reverse )
( range, range|reversed )
(
[&](auto&& range){
for (auto const& x : decltype(range)(range)) {
do_stuff(x);
}
}
);
magic_switch
将索引(std::size_t
)作为其第一个参数。它返回一个lambda,该lambda列出了一系列参数。它返回一个lambda,该lambda采用lambda并将其传递给第二个参数索引确定的第二个列表中的参数。
inline auto magic_switch( std::size_t I ) {
return [I](auto&&...options) {
return [I, &](auto&& f)->decltype(auto) {
using fptr = void(*)(void const volatile* op, decltype(f));
static const fptr table[] = {
+[](void const volatile* op_in, decltype(f) f) {
auto* option = static_cast<std::decay_t<decltype(options)>*>(op_in);
decltype(f)(f)( decltype(options)(*option) );
}...
};
const volatile void* ptrs[] = {std::addressof(options)...};
if (I >= sizeof...(options)) I = sizeof...(options)-1;
if (I == -1) return;
table[I]( ptrs[I], decltype(f)(f) );
};
};
}
是实现的草图(几乎肯定包含构建错误)。
困难的部分是"类型流"(插入术语)并不是您通常想要的方式。因此,我被迫基本上使用延续式式风格。
请注意,许多编译器对包含整个Lambda的包装扩展不满意。可以编写返回功能指针的辅助功能:
template<class F>
using f_ptr = void(*)(const volatile void*, F&&);
template<class Option, class F>
f_ptr<F> get_f_ptr() {
return +[](void const volatile* op_in, F&& f) {
auto* option = static_cast<std::decay_t<Option>*>(op_in);
std::forward<F>(f)( std::forward<Option>(*option) );
};
}
然后用:
替换表 static const fptr table[] = {
get_fptr<decltype(options), decltype(f)>()...
};
这些编译器。