Да.
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 в этих лямбдах!
Метод fancier позволил бы вам цепочки if-else блокировать бесконечно. Если бы я хотел это сделать, я использовал бы трюки с именем operator.
Во-первых, сменить 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. Я просто включаю это здесь для полноты: на самом деле написание такого рода вещей не так уж и практично, так как некоторые бедняжки должны поддерживать его, и в первые несколько раз вы пишете эту штуку, что вы собираетесь делать плохая работа!
Вы уверены, что компилятор уже не делает этого? –
@BoPersson Если оценка параметров имеет побочный эффект, их следует оценивать, даже если тело функции пуст. Также в стандартном AFAIK не указано, что пустую функцию не следует вызывать, если она пуста (это может быть сделано большинством компиляторов). – Felics
Вы, на мой взгляд, ответили на свой вопрос. – jrok