具有折叠表达式的函数的乘积

Product of functions with a fold expression

我想编写一个函数,它接受任意数量的 float(float, float) 形式的函数,并生成一个可调用对象(例如 lambda 表达式),该对象表示这些函数的乘积(在数学意义上) .

我们可以这样做,例如,通过以下方式:

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

然而,在 C++17 之前,返回的 lambda 不是 constexpr,即使 fs 是。所以,问题是:我想保留折叠表达式,但如何修改 product_of 以便返回的可调用对象是 C++14 意义上的 constexpr

如果你可以使用折叠表达式,说明你的编译器支持C++17。如果您的编译器支持 C++17,则 lambda 表达式 在可能的情况下隐式 constexpr。我假设您需要一个 C++14 解决方案。


请记住 lambda 表达式 只是 函数对象 的语法糖 operator().

这是一个使用 initializer_list 进行可变参数扩展的 C++14 解决方案。

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);
}

live example on wandbox

如果你真的想向后弯腰使用折叠表达式,你仍然可以做到。您将需要一个辅助函数 class:

#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));
    }
};

用法(使用的例子)

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);
}

这个想法与 Vittorio 的想法几乎完全相同,除了我们使用 std::tuple 以便我们可以将 product_of 函数与类型为 final 的函数对象一起使用,因为以及相同类型的多个函数。

一个std::index_sequence用来重新获取一个参数包,正好可以做一个折叠表达式


简单地转换 Vittorio 的解决方案也可以,但有我提到的注意事项(Fs 的 none 可以是最终的或相同的):

#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));
    }
};