Допустим, я хочу создать лямбду, которая выполняет некоторые другие лямбды в следующем порядке:
constexpr auto some_func{[]() {
// do something
}};
constexpr auto some_other_func{[]() {
// do something else
}};
constexpr auto funcs_tuple = std::tuple(some_func, some_other_func);
constexpr auto combined_funcs = do_funcs(funcs_tuple);
combined_funcs();
Я реализовал функцию do_funcs
как:
template <std::size_t Idx = 0, typename Tuple>
constexpr auto do_funcs(const Tuple& tup) {
return [&]() {
if constexpr (Idx < std::tuple_size_v<Tuple>) {
const auto f = std::get<Idx>(tup);
f();
do_funcs<Idx + 1>(tup)();
}
};
}
, который просто выполняет функции в кортеже по порядку. Однако результирующая переменная combined_funcs
не может быть объявлена constexpr, потому что ссылка на funcs_tuple
в вызове do_funcs
не является постоянным выражением.
Я пробую код в Compiler Explorer с лязгом (стволом) и получаем
error: constexpr variable 'combined_funcs' must be initialized by a constant expression
note: reference to 'funcs_tuple' is not a constant expression
Почему это не так считать постоянным выражением? Есть ли способ сделать это constexpr?
После некоторых проб и ошибок я обнаружил, что вместо захвата кортежа по ссылке в возвращенной лямбде из do_funcs
, а в результате захвата по значению полученная лямбда может действительно быть объявленным constexpr, но я действительно не хочу создавать копию кортежа для каждого рекурсивного вызова do_funcs
.
constexpr auto do_funcs(const Tuple& tup) {
// capture by value instead of reference
// |
// v
return [=]() { ...
Я также хотел бы создать вспомогательную функцию, которая принимает пакет параметров лямбда-выражений и отправляет его как кортеж в функцию do_funcs
следующим образом:
template <typename... Funcs>
constexpr auto make_do_funcs(Funcs&&... fs) {
const auto funcs = std::tuple(std::forward<Funcs>(fs)...);
return do_funcs(funcs);
}
Причина, по которой я использую кортежи вместо другого такого метода:
template <typename Func, typename... Funcs>
constexpr auto do_funcs(Func&& f, Funcs&&... fs) {
return [f = std::forward<Func>(f), ... fs = std::forward<Funcs>(fs)] {
f();
if constexpr (sizeof...(fs) > 0) {
do_funcs(fs...);
}
};
}
, заключается в том, что «идеальный захват» - это функция C ++ 20 и требует обходного пути с кортежами для C ++ 17.
Для получения дополнительной информации я пытаюсь создать служебную «парсер» для моей библиотеки комбинированного анализатора, которая выполняет некоторые другие парсеры для создания более сложного парсера, когда это необходимо.