각 증가분에서 어떤 방법으로 런타임 검사를 피하려면 런타임 값을 루프 구조 외부의 컴파일 시간 값으로 변환해야합니다.
이 경우에는 반복하지 않을 범위가 달라지기를 원합니다.
쉬운 방법이 방법은 몸체에 람다를 작성한 다음 선택할 루프를 선택하는 것입니다.
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
는 첫 번째 인수로 인덱스 (std::size_t
)를 취
magic_switch
(reverse)
(range, range|reversed)
(
[&](auto&& range){
for (auto const& x : decltype(range)(range)) {
do_stuff(x);
}
}
);
. 인수의 목록을 취하는 람다를 반환합니다. 이 함수는 람다를 반환하고 람다를 취하여 두 번째 목록에서 첫 번째 인수의 인덱스에 의해 결정된 두 번째 목록의 인수를 전달합니다.
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 '자동 &&'참조의 경우 완벽한 전달을 수행합니다. "범위로 선언 된 형식"으로 읽습니다. 그것은'std :: forward (range)'와 동일하지만'range'의 타입이'auto &&'또는'T &&'(forwarding reference) 인 한 절반입니다. 'range '가 값 유형 ('auto' 또는'T')이면 작동하지 않습니다. –
Yakk
@ChristopheFuzier 또한 'magic_switch'는 대신 'variant'를 기반으로 작동 할 수 있습니다. – Yakk