What is a good alternative to this C++17 fold expression in C++14?

☆樱花仙子☆ 提交于 2019-12-19 21:47:19

问题


Here is a nice, succinct fold expression based lambda in C++17:

#include <cstdint>

using ::std::uint64_t;

constexpr auto sumsquares = [](auto... n) { return ((n * n) + ...); };

// I want this to work.
uint64_t foo(uint64_t x, uint64_t y, uint64_t z)
{
    return sumsquares(x, y, z);
}
// And this too
double bar(uint64_t x, double y)
{
    return sumsquares(x, y);
}

I have this code I've written to do something similar in C++14, but it seems a lot more verbose and confusing than it should be. I'm looking for a way to express the above C++17 code in C++14 in a way that's relatively clear and succinct. To be precise, I want to be able to write code that computes the square of the magnitude of a vector for a vector of some known number of dimensions with a function-call like syntax. The number of dimensions may vary arbitrarily though. And the precise numeric type of the individual components of the coordinate system may also be arbitrary and possibly heterogeneous. But a general way to handle C++17 fold expressions in C++14 would be ideal.

#include <cstdint>
#include <utility> 

using ::std::uint64_t;

namespace {
    static constexpr struct {
        template <typename T>
        auto operator()(T && n) const
        {
           return n*n;
        }
        template <typename T, typename... S>
        auto operator()(T && n, S && ... s) const
        {
            return (n * n) + (*this)(::std::forward<S>(s)...);
        }
    } sumsquares;
}

// I want this to work.
uint64_t foo(uint64_t x, uint64_t y, uint64_t z)
{
    return sumsquares(x, y, z);
}
// And this too
double bar(uint64_t x, double y)
{
    return sumsquares(x, y);
}

回答1:


#include <utility>
#include <functional>

template<class F, class A0>
auto fold(F&&, A0&& a0) {
    return std::forward<A0>(a0);
}

template<class F, class A0, class...As>
auto fold(F&& f, A0&&a0, As&&...as) {
    return f(std::forward<A0>(a0), fold(f, std::forward<As>(as)...));
}

auto sum_squares=[](auto&&...args) {
    return fold(std::plus<>{}, (args * args)... );
};



回答2:


A possible not-recursive way to write sum() is the following

template <typename ... Ts>
auto sum (Ts ... ts)
 {
   using unused = int[];

   std::common_type_t<Ts...>  ret {};

   (void)unused{ 0, (ret += ts, 0)... };

   return ret;
 }

Given a sum() function, sum_of_squares() can be simply written as follows

template <typename ... Ts>
auto sum_of_squares (Ts ... ts)
 { return sum( (ts*ts)... ); }



回答3:


Yes, you can do simpler and more generic actually, by defining a generic sum over several objects:

template<typename T>
T sum(T x)
{
    return x;
}
template<typename U, typename... T>
auto sum(U x, T... nums)
{
    return x + sum(nums...);
}

template<typename... T>
auto sum_of_squares(T... nums)
{
    auto square = [](auto x) { return x * x; };
    return sum(square(nums)...);
}

Live demo here, and here you can see the generated assembly (which seems optimal). Although I personally really dislike auto as return type, here it allows to avoid a very complex expression to get the type.



来源:https://stackoverflow.com/questions/51005897/what-is-a-good-alternative-to-this-c17-fold-expression-in-c14

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!