如果要避免在每個增量的運行時檢查哪條路,您必須將運行時值轉換爲循環結構外的編譯時間值。
在這種情況下,我們希望我們循環的範圍有所不同,而身體則不會。
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