我可以使用折叠表达式实现 max(A, max(B, max(C, D))) 吗?
Can I implement max(A, max(B, max(C, D))) using fold expressions?
在尝试使用 C++17 折叠表达式时,我尝试实现 max 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)));
可以吗?
只是为了玩玩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)), ... );
}
与您的原始版本没有太大区别。
可能不是您想听到的,但不是。使用折叠表达式不可能做到这一点(纯粹1)。他们的语法根本不允许这样做:
A fold expression performs a fold of a template parameter pack over a
binary operator.
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。 :-]
I would like to write equivalent function using fold expressions and std::max
. For example for 3 elements it should expand to
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;
}
由于还没有人将此问题作为答案发布,因此最简单的方法就是使用针对此问题现成的 std::max()
重载:需要一个initializer_list
:
template<typename... T>
constexpr size_t max_sizeof() {
return std::max({sizeof(T)...});
}
只是为了好玩,对 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)助手class只使用类型的sizeof()
(直接在max_sizeof()
中解析,(b)不使用基于终端值在 void
和零上,(c) operator^()
已声明但未实现(不需要实现它:仅对 return 类型感兴趣)和 (d) max_sizeof()
使用 decltype()
而不是调用 operator^()
(因此不需要实现它)。
好的,没问题。
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 正确等
这个怎么样(由 https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html 提供):
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
在尝试使用 C++17 折叠表达式时,我尝试实现 max 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)));
可以吗?
只是为了玩玩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)), ... );
}
与您的原始版本没有太大区别。
可能不是您想听到的,但不是。使用折叠表达式不可能做到这一点(纯粹1)。他们的语法根本不允许这样做:
A fold expression performs a fold of a template parameter pack over a binary operator.
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。 :-]
I would like to write equivalent function using fold expressions and
std::max
. For example for 3 elements it should expand to
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;
}
由于还没有人将此问题作为答案发布,因此最简单的方法就是使用针对此问题现成的 std::max()
重载:需要一个initializer_list
:
template<typename... T>
constexpr size_t max_sizeof() {
return std::max({sizeof(T)...});
}
只是为了好玩,对 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)助手class只使用类型的sizeof()
(直接在max_sizeof()
中解析,(b)不使用基于终端值在 void
和零上,(c) operator^()
已声明但未实现(不需要实现它:仅对 return 类型感兴趣)和 (d) max_sizeof()
使用 decltype()
而不是调用 operator^()
(因此不需要实现它)。
好的,没问题。
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 正确等
这个怎么样(由 https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html 提供):
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