C++ 我可以使用折叠表达式实现max(A,max(B,max(C,D))?
在尝试使用C++17折叠表达式时,我尝试实现maxC++ 我可以使用折叠表达式实现max(A,max(B,max(C,D))?,c++,templates,variadic-templates,c++17,fold-expression,C++,Templates,Variadic Templates,C++17,Fold Expression,在尝试使用C++17折叠表达式时,我尝试实现maxsizeof,其中结果是最大的sizeof类型。 我有一个丑陋的折叠版本,它使用变量和lambda,但是我想不出一种方法来使用折叠表达式和std::max()来获得相同的结果 这是我的折叠版本: template<typename... T> constexpr size_t max_sizeof(){ size_t max=0; auto update_max = [&max](const size_t&am
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);
可以这样做吗?只需使用c++17倍的表达式即可
template <typename ... Ts>
constexpr std::size_t max_sizeof ()
{
std::size_t ret { 0 };
return ( (ret = (sizeof(Ts) > ret ? sizeof(Ts) : ret)), ... );
}
与您的原始版本没有什么不同。可能不是您想要听到的,但不是。使用折叠表达式不可能做到这一点(纯1)。他们的语法根本不允许这样做: 折叠表达式将模板参数包折叠到 二进制运算符
折叠表达式:
(强制转换表达式折叠运算符…)
(…折叠运算符强制转换表达式)
(强制转换表达式折叠运算符…折叠运算符强制转换表达式)
折叠运算符:其中一个
+ - * / % ^ & | >
+= -= *= /= %= ^= &= |= = =
== != < > = && || , .* ->*
因为函数调用表达式不是纯语法意义上的二进制运算符
1参考其他精彩答案。
如果您想在这里使用折叠表达式,那么您需要以某种方式使用操作符来调用
std::max
,而不是函数调用。下面是一个滥用操作员^
的例子:
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>{} ^ ...);
}
从外部看,这两种实现是等效的;就实施而言,我个人更喜欢前者,但YMMV.:-]
我想使用折叠表达式和std::max
编写等效函数。例如,对于3个元素,它应该扩展到
返回std::max(sizeof(A)、std::max(sizeof(B)、sizeof(C))代码>
另一种可能的解决方案(基于fold表达式的递归,not)如下
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...>()); }
模板
constexpr std::size\u t max\u sizeof()
{返回sizeof(T0);}
模板
constexpr std::size\u t max\u sizeof()
{return std::max(sizeof(T0),max_sizeof());}
不是折叠表达式,而是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;
}
模板
constexpr std::size\u t max\u sizeof()
{
自动基准=标准::最大值(sizeof(X),sizeof(Y));
如果constexpr(sizeof…(Ts)==0)
{
//没什么
}
如果constexpr(sizeof…(Ts)==1)
{
基准=标准::最大值(基准,尺寸);
}
其他的
{
base=std::max(base,max_sizeof());
}
返回基地;
}
由于目前还没有人将此作为答案发布,因此最简单的方法就是使用为该问题准备的重载:使用初始值设定项列表的重载:
template<typename... T>
constexpr size_t max_sizeof() {
return std::max({sizeof(T)...});
}
模板
constexpr size\u t max\u sizeof(){
返回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; }
名称空间详细信息
{
模板
struct tSizeH:std::integral_常量{};
模板
constexpr tSizeH操作员^(tSizeH,tSizeH);
}
模板
constexpr std::size\t max\u sizeof()无例外
{返回decltype((detail::tSizeH{}^…)::value;}
有点简化,因为(A)helper类只使用类型的sizeof()
(直接在max_sizeof()
中解析),(b)不使用基于void
和零的终值,(c)声明了运算符^()
,但未实现(不需要实现它:仅对返回类型感兴趣)和(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)}; }
整天写东西很烦人,所以
#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();
}
模板
自动结果3(Xs…Xs){
return(可折叠(重载((std::max))*…*xs.get();
}
甚至
template<class...Xs>
constexpr auto result4( Xs... xs )
RETURNS( (foldable(OVERLOADS_OF((std::max))) * ... * xs).get() )
模板
constexpr自动结果4(Xs…Xs)
返回((可折叠(重载((std::max))*…*xs.get())
哪个更具表现力,并且没有异常/constexpr,等等。这个怎么样(由提供):
模板
constexpr auto max(const U&U,const V&…V)->typename std::common_type::type{
使用rettype=typename std::common_type::type;
rettype结果=静态施法(u);
(void)std::初始值设定项_list{((v>result)?(result=static_cast(v),0):0);
返回结果;
}
在c++14中工作,因此它不使用c++17折叠表达式,但它的工作方式如下所示:max(std::initializer_list)
存在。是否有理由使用折叠,而不仅仅是模板constexpr size_t max_sizeof(){return std::max({sizeof(t)}}
@DaveS为什么不工作?@schorsch312:那么它就不能是constepr
模板constepr std::size\t max\u sizeof=sizeof(std::aligned\u union\t)
这不容易阅读…没有办法将其重写为折叠递归形式吗?我对技术上的正确性投了赞成票,但我希望有人能想出更好的解决方案,因为像YSC一样,我在解析这一问题时遇到了困难。我的意思是,我可以猜出它现在的作用,因为我问了这个问题,但如果这段代码来给我审阅,我会的糊涂了。:@YSC-嗯。。。不是很聪明,但是(iMHO)对于一个熟练的C++程序员来说应该是容易阅读的;无论如何,我添加了一个版本,它使用了std::max()
,应该更简单;但是“fold recursive form”@nosensetal是什么意思?不幸的是,新的c++17 fold表达式可以与运算符一起工作;我所能想到的最好的方法是使用逗号作为运算符,在每个sizeof()
上迭代某些内容;“某物”可以是基于te的赋值
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() )
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;
}