我可以使用折叠表达式实现Max(A Max(B,Max(C,D)))

Can I implement max(A, max(B, max(C, D))) using fold expressions?

本文关键字:Max 可以使 表达式 实现 我可以 折叠      更新时间:2023-10-16

尝试使用C 17倍表达式时,我尝试实现最大sizeof,结果最多是类型的sizeof。我有一个使用变量和lambda的丑陋折叠版本,但是我无法想到一种使用折叠表达式的方法,并且std::max()获得相同的结果。

这是我的折叠版本:

template<typename... T>
constexpr size_t max_sizeof(){
    size_t max=0;
    auto update_max = [&max](const size_t& size) {if (max<size) max=size; };
    (update_max(sizeof (T)), ...);
    return max;
}

static_assert(max_sizeof<int, char, double, short>() == 8);
static_assert(max_sizeof<char, float>() == sizeof(float));
static_assert(max_sizeof<int, char>() == 4);

我想使用折叠表达式和std::max()编写等效函数。例如,对于3个元素,它应该扩展到

return std::max(sizeof (A), std::max(sizeof(B), sizeof (C)));

可以做到吗?

由于没有人将此作为答案,因此最少的努力做到这一点的方法就是使用现成的问题的std::max()的过载:服用initializer_list

template<typename... T>
constexpr size_t max_sizeof() {
    return std::max({sizeof(T)...});
}

可能不是您想听的,而是。不可能使用折叠表达式(纯 1 )这样做。他们的语法根本不允许:

[expr.prim.fold]

折叠表达式执行模板参数包的折叠 二进制操作员

fold-expression:
  ( cast-expression fold-operator ... )
  ( ... fold-operator cast-expression )
  ( cast-expression fold-operator ... fold-operator cast-expression )
fold-operator: one of
  +   -   *   /   %   ^   &   |   <<   >> 
  +=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  =
  ==  !=  <   >   <=  >=  &&  ||  ,    .*   ->*

仅仅因为函数调用表达式不是纯语法中的二进制操作员。


1 请参阅其他出色的答案。

如果要在此处使用折叠表达式,则需要以某种方式使用操作员调用std::max而不是函数调用。这是一个滥用operator^的示例:

namespace detail {
    template<typename T, std::size_t N = sizeof(T)>
    struct type_size : std::integral_constant<std::size_t, N> { };
    template<typename T, auto M, typename U, auto N>
    constexpr auto operator ^(type_size<T, M>, type_size<U, N>) noexcept {
        return type_size<void, std::max(M, N)>{};
    }
}
template<typename... T>
constexpr std::size_t max_sizeof() noexcept {
    using detail::type_size;
    return (type_size<T>{} ^ ... ^ type_size<void, 0>{});
    // or, if you don't care to support empty packs
    // return (type_size<T>{} ^ ...);
}

在线演示


编辑: @Barry从type_size删除T的建议(此处更名为max_val):

namespace detail {
    template<auto N>
    struct max_val : std::integral_constant<decltype(N), N> { };
    template<auto M, auto N, auto R = std::max(M, N)>
    constexpr max_val<R> operator ^(max_val<M>, max_val<N>) noexcept {
        return {};
    }
}
template<typename... T>
constexpr std::size_t max_sizeof() noexcept {
    using detail::max_val;
    return (max_val<sizeof(T)>{} ^ ... ^ max_val<std::size_t{}>{});
    // or, if you don't care to support empty packs
    // return (max_val<sizeof(T)>{} ^ ...);
}

在线演示

在外部,这两个实现都是等效的;在实施方面,我个人更喜欢前者,但YMMV。: - ]

只是为了使用C 17倍表达式

template <typename ... Ts>
constexpr std::size_t max_sizeof ()
 {
   std::size_t  ret { 0 };
   return ( (ret = (sizeof(Ts) > ret ? sizeof(Ts) : ret)), ... ); 
 }

或使用std::max()constexpr从C 14开始的事实(因此它在C 17中)

template <typename ... Ts>
constexpr std::size_t max_sizeof ()
 {
   std::size_t  ret { 0 };
   return ( (ret = std::max(sizeof(Ts), ret)), ... ); 
 }

与您的原始版本没有什么不同。

当然没有问题。

template<class Lhs, class F>
struct foldable_binop_t {
  Lhs lhs;
  F f;
  template<class Rhs>
  auto operator*(Rhs&& rhs) &&
  -> foldable_binop_t< std::result_of_t<F&(Lhs&&, Rhs&&)>, F >
  {
    return { f(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs)), std::forward<F>(f) };
  }
  Lhs operator()() && { return std::forward<Lhs>(lhs); }
  operator Lhs() && { return std::move(*this)(); }
  Lhs get() && { return std::move(*this); }
};
template<class F>
struct foldable_t {
  F f;
  template<class Lhs>
  friend foldable_binop_t<Lhs, F> operator*( Lhs&& lhs, foldable_t&& self ) {
    return {std::forward<Lhs>(lhs), std::forward<F>(self.f)};
  }
  template<class Rhs>
  foldable_binop_t<Rhs, F> operator*( Rhs&& rhs ) && {
    return {std::forward<Rhs>(rhs), std::forward<F>(f)};
  }
};
template<class F>
foldable_t<F> foldable(F f) { return {std::move(f)}; }

测试代码:

template<class...Xs>
auto result( Xs... xs ) {
  auto maxer = [](auto&&...args){return (std::max)(decltype(args)(args)...);};
  return ((0 * foldable(maxer)) * ... * xs).get();
}
template<class...Xs>
auto result2( Xs... xs ) {
  auto maxer = [](auto&&...args){return (std::max)(decltype(args)(args)...);};
  return (foldable(maxer) * ... * xs).get();
}
int main() {
  int x = result2( 0, 7, 10, 11, -3 ); // or result
  std::cout << x << "n";
}

现场示例。

我个人发现

  auto maxer = [](auto&&...args){return (std::max)(decltype(args)(args)...);};

一直很烦人,所以

#define RETURNS(...) 
  noexcept(noexcept(__VA_ARGS__)) 
  -> decltype(__VA_ARGS__) 
  { return __VA_ARGS__; }
#define OVERLOADS_OF(...) 
  [](auto&&...args) 
  RETURNS( __VA_ARGS__( decltype(args)(args)... ) )

成为

template<class...Xs>
auto result3( Xs... xs ) {
  return (foldable(OVERLOADS_OF((std::max))) * ... * xs).get();
}

甚至

template<class...Xs>
constexpr auto result4( Xs... xs )
  RETURNS( (foldable(OVERLOADS_OF((std::max))) * ... * xs).get() )

似乎更具表现力,并且获得了noexcept/constexpr,等等。

我想使用折叠表达式和std::max编写等效函数。例如,对于3个元素,它应该扩展到

return std::max(sizeof (A), std::max(sizeof(B), sizeof (C)));

另一个可能的解决方案(基于递归,不是折叠表达式的)是以下

template <typename T0>
constexpr std::size_t max_sizeof ()
 { return sizeof(T0); }
    
template <typename T0, typename T1, typename ... Ts>
constexpr std::size_t max_sizeof ()
 { return std::max(sizeof(T0), max_sizeof<T1, Ts...>()); }

不是折叠表达式,而是C 17提供的另一种方式-if constexpr

template<class X, class Y, class...Ts>
constexpr std::size_t max_sizeof()
{
    auto base = std::max(sizeof(X), sizeof(Y));
    if constexpr (sizeof...(Ts) == 0)
    {
        // nothing
    }
    else if constexpr (sizeof...(Ts) == 1)
    {
        base = std::max(base, sizeof(Ts)...);
    }
    else
    {
        base = std::max(base, max_sizeof<Ts...>());
    }
    return base;
}

只是为了娱乐,iLdjarn

的出色解决方案上的主题变体
namespace detail
 {
   template <std::size_t N>
   struct tSizeH : std::integral_constant<std::size_t, N> { };
   template <std::size_t M, std::size_t N>
   constexpr tSizeH<std::max(M, N)> operator^ (tSizeH<M>, tSizeH<N>);
 }
template <typename ... T>
constexpr std::size_t max_sizeof() noexcept
 { return decltype((detail::tSizeH<sizeof(T)>{} ^ ...))::value; }

有点简化,因为(a)辅助类仅使用类型的sizeof()(直接在max_sizeof()中解决,(b)不使用基于void和零的终端值的使用,(c)operator^()被声明但未完成(无需实现它:仅对返回类型的利息)和(d)max_sizeof()使用decltype()而不是调用operator^()(因此无需实施)。

怎么样

template<typename U, typename ... V>
constexpr auto max(const U &u, const V &... v) -> typename std::common_type<U, V...>::type {
  using rettype = typename std::common_type<U, V...>::type;
  rettype result = static_cast<rettype>(u);
  (void)std::initializer_list<int>{ ( (v > result)?(result = static_cast<rettype>(v), 0):0 )... };
  return result;
}

在C 14中工作,因此不使用C 17倍表达式,但如下所示:https://godbolt.org/z/6OWVK9