如果您想避免在每次增量时检查运行时要走的路,则必须将运行时值转换为循环结构之外的编译时值。
在这种情况下,我们希望循环的范围发生变化,而主体则不会。
最简单的方法是为 body 编写一个 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);
}
}
);
wheremagic_switch
将索引 ( std::size_t
) 作为其第一个参数。它返回一个 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)>()...
};
在那些编译器上。