Избегайте вызова функции при ложном условии времени компиляции

Я хочу быть в состоянии избежать вызова функции, когда условие ложно, когда это известно во время компиляции. Теперь я использую что-то вроде этого:

template<bool Enabled>
void fun(params)
{
//do nothing
}

template<>
void fun<true>(params)
{
//do something with params.
}

Что мне не нравится в этом подходе, так это то, что params оцениваются, даже если тело функции пусто.

Я хотел бы получить решение, когда функция вообще не вызывается, и параметры не будут оцениваться, когда условие ложно (это может быть оптимизировано в первом случае с пустой функцией, но я не могу предположить, что это верно для каждого компилятор).

Это вообще возможно?

3

Решение

Ага.

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. Я просто включил это здесь для полноты: на самом деле написание такого рода вещи не так уж и практично, поскольку некоторые бедняги должны будут поддерживать это, и первые несколько раз, когда вы пишете эти вещи, вы собираетесь делать плохая работа!

13

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]