Произведение функций со складным выражением

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

Мы можем сделать это, например, следующим образом:

template<typename... BinaryFunctions>
auto product_of(BinaryFunctions... fs) {
return [=](float x, float y) { return (... * fs(x, y)); };
}

Однако до C ++ 17 возвращаемая лямбда не constexpr даже когда fs являются. Итак, вопрос: я хочу сохранить выражение сгиба, но как я могу изменить product_of так что возвращаемый вызываемый объект является constexpr в смысле C ++ 14?

0

Решение

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

#include <tuple>
#include <utility>

template<typename... Fs>
class product_of_fn
{
std::tuple<Fs...> fs;
public:
template<typename... FsFwd>
constexpr explicit product_of_fn(FsFwd &&... fs)
: fs{ std::forward<FsFwd>(fs)... }
{}

constexpr auto operator()(float x, float y) {
return impl(x, y, std::make_index_sequence<sizeof...(Fs)>{});
}
private:
template<std::size_t... Is>
constexpr auto impl(float x, float y, std::index_sequence<Is...>) {
return (... * std::get<Is>(fs)(x, y));
}
};

Использование (используя @VittorioRomeoпример)

template<typename... Fs>
constexpr auto product_of(Fs... fs) {
return product_of_fn<std::decay_t<Fs>...>{ std::forward<Fs>(fs)... };
}

template<int>
struct adder
{
constexpr auto operator()(float x, float y) { return x + y; }
};

int main()
{
auto f = product_of(adder<0>{}, adder<1>{});

static_assert(f(1, 2) == 3 * 3);
}

Идея почти такая же, как у Витторио, за исключением того, что мы используем std::tuple так что мы можем использовать product_of функция с функциональными объектами, чьи типы final, а также несколько функций одного типа.

std::index_sequence используется для повторного получения пакета параметров, чтобы мы могли сделать выражение сгиба.


Простое преобразование решения Витторио также работает, но с оговорками, которые я упомянул (ни один из Fs может быть окончательным или одинаковым):

#include <utility>

template<typename... Fs>
class product_of_fn : Fs...
{
public:
template<typename... FsFwd>
constexpr explicit product_of_fn(FsFwd &&... fs)
: Fs{ std::forward<FsFwd>(fs) }...
{}

constexpr auto operator()(float x, float y) {
return (... * static_cast<Fs &>(*this)(x, y));
}
};
1

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

Если вы можете использовать сложить выражения, это означает, что ваш компилятор поддерживает C ++ 17. Если ваш компилятор поддерживает C ++ 17, лямбда-выражения неявно constexpr где возможно. Я предполагаю, что вам нужно решение C ++ 14.


Просто запомни это лямбда-выражения просто синтаксический сахар для функциональные объекты с перегрузкой operator(),

Вот решение C ++ 14, которое использует initializer_list для вариационного расширения.

template <typename... TFs>
struct product_of_fn : TFs...
{
template <typename... TFFwds>
constexpr product_of_fn(TFFwds&&... fs) : TFs(std::forward<TFFwds>(fs))... { }

constexpr auto operator()(float x, float y)
{
std::initializer_list<float> results{static_cast<TFs&>(*this)(x, y)...};
float acc = 1;
for(auto x : results) acc *= x;
return acc;
}
};

Использование:

template<int>
struct adder
{
constexpr auto operator()(float x, float y){ return x + y; }
};

template<typename... TFs>
constexpr auto product_of(TFs&&... fs) {
return product_of_fn<std::decay_t<TFs>...>(std::forward<TFs>(fs)...);
}

int main()
{
auto f = product_of(adder<0>{}, adder<1>{});

static_assert(f(1, 2) == 3 * 3);
}

живой пример на wandbox

2