如果要避免在每个增量的运行时检查哪条路,您必须将运行时值转换为循环结构外的编译时间值。
在这种情况下,我们希望我们循环的范围有所不同,而身体则不会。
easy这样做的方法是为身体写一个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
的Index(std::size_t
)作为第一个参数。它返回一个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的包扩展不满意。返回函数指针辅助函数可以写成:在这些编译器
static const fptr table[] = {
get_fptr<decltype(options), decltype(f)>()...
};
:
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));
};
}
然后用替换表。
哇!谢谢!我明白了,但我会尝试了解魔术开关的实施细节。 “decltype(范围)(范围)”的含义是什么?这是一个演员,为什么需要? –
@ChristopheFuzier对于'auto &&'参考,它可以完美转发。阅读它作为“范围作为它声明的类型”。它相当于'std :: forward(range)',但只有'range'是'auto &&'或'T &&'(转发引用)类型的一半长。如果'range'是一个值类型('auto'或'T'),它不起作用。 –
Yakk
@ChristopheFuzier另外请注意,'magic_switch'可以基于变体工作。 – Yakk