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

时间:2017-04-05 22:52:32

标签: c++ variadic-templates constexpr c++17 fold

我想编写一个函数,它接受template<typename... BinaryFunctions> auto product_of(BinaryFunctions... fs) { return [=](float x, float y) { return (... * fs(x, y)); }; } 形式的任意数量的函数,并产生一个可调用的对象(例如一个lambda表达式),它代表这些函数的乘积(在数学意义上)。 / p>

我们可以通过以下方式执行此操作:

constexpr

但是,在C ++ 17之前,即使fsproduct_of,返回的lambda也不是constexpr。所以,问题是:我想保留fold表达式,但是如何修改{{1}}使得返回的可调用对象在C ++ 14意义上是{{1}}?

2 个答案:

答案 0 :(得分:2)

如果您可以使用折叠表达式,则表示您的编译器支持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

答案 1 :(得分:1)

如果你真的想向后弯腰以使用折叠表达式,你仍然可以这样做。您将需要一个辅助功能类:

#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&#39}示例)

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