Я хочу быть в состоянии избежать вызова функции, когда условие ложно, когда это известно во время компиляции. Теперь я использую что-то вроде этого:
template<bool Enabled>
void fun(params)
{
//do nothing
}
template<>
void fun<true>(params)
{
//do something with params.
}
Что мне не нравится в этом подходе, так это то, что params
оцениваются, даже если тело функции пусто.
Я хотел бы получить решение, когда функция вообще не вызывается, и параметры не будут оцениваться, когда условие ложно (это может быть оптимизировано в первом случае с пустой функцией, но я не могу предположить, что это верно для каждого компилятор).
Это вообще возможно?
Ага.
template<bool b, typename Func, typename FuncElse>
struct compile_time_if_functor {
void operator()( Func&& f, FuncElse&& ) const{
std::forward<Func>(f)();
}
};
template<typename Func, typename FuncElse>
struct compile_time_if_functor<false, Func, FuncElse> {
void operator()( Func&&, FuncElse&& else_f ) const{
std:forward<FuncElse>(else_f)();
}
};
template<bool b, typename Func, typename Else>
void compile_time_if( Func&& f, Else&& elsef ) {
compile_time_if_functor<b, Func, Else> functor;
functor(
std::forward<Func>(f),
std::forward<Else>(elsef)
);
}
template<bool b, typename Func>
void compile_time_if( Func&& f ) {
auto do_nothing = []{};
compile_time_if<b>( std::forward<Func>(f), std::move(do_nothing) );
}
использовать:
int main() {
compile_time_if<expression>([&]{
// code that runs iff expression is true
});
compile_time_if<expression2>([&]{
// code that runs iff expression2 is true
},[&]{
// else clause, runs iff expression2 is false
});
}
обратите внимание, что код внутри {}
компилируется, но не запускается, если находится в неправильной ветке if
, Таким образом, этот код должен быть правильно сформированным и легальным на уровне типов, но он не должен быть легальным для выполнения во время выполнения. Не надо создавать массивы размером 0 в этих лямбдах!
Причудливый метод позволит вам связывать блоки if-else на неопределенный срок. Я бы использовал трюки с именованными операторами, если бы хотел это сделать.
Во-первых, изменить compile_time_if
возвращать std::integral_constant< bool, b >
,
Затем написать compile_time_else(Func&&)
а также compile_time_elseif<bool>(Func&&)
которые возвращают типы того пакета, который Func
и переопределить operator*( std::true_type, X )
а также operator*( std::false_type, X )
бежать или не бежать Func
и вернуться std::true_type
или же std::false_type
,
Конечной целью будет следующий синтаксис:
If<expression>([&]{
// block 1
})*Else([&]{
// block 2
});
If<expression>([&]{
// block 1
})*ElseIf<expression2>([&]{
// block 2
})*ElseIf<expression3>([&]{
// block 3
})*Else([&]{
// block 4
});
позволяя полностью каскадное управление потоком. Вы могли бы даже пойти так далеко, чтобы сделать:
compile_time_switch<value>(
Case<c0, FallThrough>(),
Case<c1>([&]{
// block 1
}),
Case<c2, Continue>([&]{
// block 2
}),
Case<c3>([&]{
// block 3
}),
Case<c4>([&]->CanContinue{
// block 4
if (condition) {
return Continue;
} else {
return Break;
}
}),
Case<c4>([&]{
}),
Default([&]{
})
};
но это забегает вперед.
Для идей о том, как возиться с дублированием синтаксиса C ++ таким образом, который позволяет компилятору манипулировать управлением потоком во время компиляции, посмотрите на boost Phoenix. Я просто включил это здесь для полноты: на самом деле написание такого рода вещи не так уж и практично, поскольку некоторые бедняги должны будут поддерживать это, и первые несколько раз, когда вы пишете эти вещи, вы собираетесь делать плохая работа!
Других решений пока нет …