2013-04-27 2 views
3

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

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

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

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

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

Возможно ли это?

+1

Вы уверены, что компилятор уже не делает этого? –

+0

@BoPersson Если оценка параметров имеет побочный эффект, их следует оценивать, даже если тело функции пуст. Также в стандартном AFAIK не указано, что пустую функцию не следует вызывать, если она пуста (это может быть сделано большинством компиляторов). – Felics

+0

Вы, на мой взгляд, ответили на свой вопрос. – jrok

ответ

13

Да.

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

+0

Я думаю, вы должны прямо указать, что код во всех этих лямбдах должен быть хорошо сформирован, также вы можете упомянуть его с частью «скомпилировано». – Xeo

+0

И теперь добавьте макроса магии для 'If (выражение) {} Else {};' (работает с оператором присваивания);) – dyp

+0

@DyP Ба, макросы злы. ;) '#define If (выражение) compile_time_if () = [&]' и '#define Else * compile_time_else * [&]' и '#define ElseIf (выражение) * compile_time_else_if * [&]' с большим количеством шаблонов плагинов и перегрузок за ним. – Yakk

Смежные вопросы