If you really want to bend over backwards to use a fold expression, you can still do it. You will need an auxiliary function 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));
    }
};
Usage (using @VittorioRomeo's example)
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);
}
The idea is pretty much exactly the same as Vittorio's, except we use a std::tuple so that we can use the product_of function with function objects whose types are final, as well as multiple functions of the same type.
A std::index_sequence is used to re-obtain a parameter pack just so that we can do a fold expression.
Simply converting Vittorio's solution works as well, but with the caveats I mentioned (none of Fs can be final or the same):
#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));
    }
};