各インクリメントで実行時のチェックを避けたい場合は、実行時の値をループ構造外のコンパイル時の値に変換する必要があります。
この場合、私たちはループしている範囲を変化させたい、ボディは変化させたくない。
easyこれを行うには、ボディのラムダを書いてから、選択するループを選択するスイッチを用意します。
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);
}
私たちはループ外でランタイムディスパッチを行い、どのようにループするかについての静的なタイプの情報を持つ2つの異なるループを作成しました。
私たちは、このようなアダプタを作ることができます。
magic_switch
(reverse)
(range, range|reversed)
(
[&](auto&& range){
for (auto const& x : decltype(range)(range)) {
do_stuff(x);
}
}
);
magic_switch
はその最初の引数としてインデックス(std::size_t
)をとります。それは引数のリストを取るラムダを返します。これはラムダを返し、ラムダを受け取り、そのリストに最初の引数のインデックスによって決定されるように、2番目のリストから引数を渡します。
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));
};
};
}
は実装時のスケッチです(ほとんどの場合、ビルドエラーが含まれています)。
難しい部分は、「タイプフロー」(用語をコインにする)が、通常はそれが欲しいとは思わない方法です。だから、私は基本的に連続通行スタイルを使わなければならない。
多くのコンパイラはラムダ全体を含むパック展開に不満を持っていることに注意してください。関数ポインタを返すヘルパー関数を書くことができます。
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)>()...
};
。
うわー!ありがとうございました!アイデアが得られますが、私はあなたの魔法のスイッチの実装の詳細を理解しようとします。 'decltype(range)(range)'のポイントは何ですか?これはキャストですか、なぜそれが必要ですか? –
@ChristopheFuzier 'auto &&'リファレンスのために、それは完全な転送を行います。 "それが宣言された型としての範囲"と読んでください。これは 'std :: forward(range)'に相当しますが、 'range'の型が' auto && 'または' T && '(forwarding reference)である限り半分です。 'range'が値型(' auto'または 'T')であれば動作しません。 –
Yakk
@ChristopheFuzierまた、 'magic_switch'は代わりに' variant'sに基づいて動作することに注意してください。 – Yakk