给定表达式模板树,我想在处理它之前创建一个新的优化树。请考虑以下乘法运算示例:
a * b * c * d,
由于operator*
从左到右的关联性而产生表达式树:
(((a * b) * c) * d).
我想生成一个转换的表达式树,其中乘法从右到左发生:
(a * (b * (c * d))).
考虑二进制表达式类型:
template<typename Left, typename Right>
struct BinaryTimesExpr
{
BinaryTimesExpr() = default;
BinaryTimesExpr(const BinaryTimesExpr&) = default;
BinaryTimesExpr(BinaryTimesExpr&&) = default;
BinaryTimesExpr(Left&& l, Right&& r) : left(forward<Left>(l)), right(forward<Right>(r)) {}
BinaryTimesExpr& operator=(const BinaryTimesExpr&) = default;
BinaryTimesExpr& operator=(BinaryTimesExpr&&) = default;
Left left;
Right right;
};
定义乘法运算符operator*
:
template<typename Left, typename Right>
BinaryTimesExpr<Constify<Left>, Constify<Right>> operator*(Left&& l, Right&& r)
{
return {forward<Left>(l), forward<Right>(r)};
}
其中Constify
由:
template<typename T> struct HelperConstifyRef { using type = T; };
template<typename T> struct HelperConstifyRef<T&> { using type = const T&; };
template<typename T>
using ConstifyRef = typename HelperConstifyRef<T>::type;
用于确保从lvalues构造时具有const lvalue-references的子表达式,以及从rvalues构造时rvalues的副本(通过复制/移动)。
定义转换函数,该函数使用以前的条件创建新的表达式模板树:
template<typename Expr>
auto Transform(const Expr& expr) -> Expr
{
return expr;
}
template<typename Left, typename Right>
auto Transform(const BinaryTimesExpr<Left, Right>& expr) -> type(???)
{
return {(Transform(expr.left), Transform(expr.right))};
}
template<typename Left, typename Right>
auto Transform(const BinaryTimesExpr<BinaryTimesExpr<LeftLeft, LeftRight>, Right>& expr) -> type(???)
{
return Transform({Transform(expr.left.left), {Transform(expr.left.right), Transform(expr.right)}}); // this sintax is invalid...how can I write this?
}
我的问题是:
1)如何确定Transform
函数的返回类型?我尝试过使用类型特征:
template<typename Expr>
struct HelperTransformedExpr
{
using type = Expr;
};
template<typename Left, typename Right>
struct HelperTransformedExpr<BinaryTimesExpr<Left, Right>>
{
using type = BinaryTimesExpr<typename HelperTransformedExpr<Left>::type, typename HelperTransformedExpr<Right>::type>;
};
template<typename LeftLeft, typename LeftRight, typename Right>
struct HelperTransformedExpr<BinaryTimesExpr<BinaryTimesExpr<LeftLeft, LeftRight>, Right>>
{
using type = BinaryTimesExpr<typename HelperTransformedExpr<LeftLeft>::type,
BinaryTimesExpr<typename HelperTransformedExpr<LeftRight>::type, typename HelperTransformedExpr<Right>::type>>;
};
template<typename Expr>
using TransformedExpr = typename HelperTransformedExpr<Expr>::type;
但不知道如何应用它来解决我的问题(2)。
2)如何编写递归行:
return Transform({Transform(expr.left.left), {Transform(expr.left.right), Transform(expr.right)}});
3)是否有针对此问题的清洁解决方案?
编辑: DyP提供了上述问题的部分解决方案。以下是基于他答案的完整解决方案:
template<typename Expr>
auto Transform(const Expr& expr) -> Expr
{
return expr;
}
template<typename Left, typename Right>
auto Transform(BinaryTimesExpr<Left, Right> const& expr)
-> decltype(BinaryTimesExpr<decltype(Transform(expr.left)), decltype(Transform(expr.right))>{Transform(expr.left), Transform(expr.right)})
{
return BinaryTimesExpr<decltype(Transform(expr.left)), decltype(Transform(expr.right))>{Transform(expr.left), Transform(expr.right)};
}
template<typename LeftLeft, typename LeftRight, typename Right>
auto Transform(BinaryTimesExpr<BinaryTimesExpr<LeftLeft, LeftRight>, Right> const& expr)
-> decltype(Transform(BinaryTimesExpr<decltype(Transform(expr.left.left)), BinaryTimesExpr<decltype(Transform(expr.left.right)), decltype(Transform(expr.right))>>{Transform(expr.left.left), {Transform(expr.left.right), Transform(expr.right)}}))
{
return Transform(BinaryTimesExpr<decltype(Transform(expr.left.left)), BinaryTimesExpr<decltype(Transform(expr.left.right)), decltype(Transform(expr.right))>>{Transform(expr.left.left), {Transform(expr.left.right), Transform(expr.right)}});
}
int main()
{
BinaryTimesExpr<int, int> beg{1,2};
auto res = beg*3*4*5*beg;
std::cout << res << std::endl;
std::cout << Transform(res) << std::endl;
}
输出:
(((((1*2)*3)*4)*5)*(1*2))
(1*(2*(3*(4*(5*(1*2))))))
请注意,除了最外部Transform
调用之外,有必要对每个子表达式应用Transform
函数(请参阅上一次Transform
重载)。
可以找到完整的源代码here。
答案 0 :(得分:3)
虽然OP想要一个没有使用Boost.Proto的解决方案,但其他人可能会有兴趣看看如何(很容易)使用它来完成:
#include <iostream>
#include <boost/type_traits/is_same.hpp>
#include <boost/proto/proto.hpp>
namespace proto = boost::proto;
using proto::_;
struct empty {};
struct transform
: proto::reverse_fold_tree<
_
, empty()
, proto::if_<
boost::is_same<proto::_state, empty>()
, _
, proto::_make_multiplies(_, proto::_state)
>
>
{};
int main()
{
proto::literal<int> a(1), b(2), c(3), d(4);
proto::display_expr( a * b * c * d );
proto::display_expr( transform()(a * b * c * d) );
}
上面的代码显示:
multiplies(
multiplies(
multiplies(
terminal(1)
, terminal(2)
)
, terminal(3)
)
, terminal(4)
)
multiplies(
terminal(1)
, multiplies(
terminal(2)
, multiplies(
terminal(3)
, terminal(4)
)
)
)
答案 1 :(得分:0)
没有合并完美转发:
#include <iostream>
// simplified by making it an aggregate
template<typename Left, typename Right>
struct BinaryTimesExpr
{
Left left;
Right right;
};
// "debug" / demo output
template<typename Left, typename Right>
std::ostream& operator<<(std::ostream& o, BinaryTimesExpr<Left, Right> const& p)
{
o << "(" << p.left << "*" << p.right << ")";
return o;
}
// NOTE: removed reference as universal-ref yields a reference type for lvalues!
template<typename Left, typename Right>
BinaryTimesExpr < typename std::remove_reference<Left>::type,
typename std::remove_reference<Right>::type >
operator*(Left&& l, Right&& r)
{
return {std::forward<Left>(l), std::forward<Right>(r)};
}
// overload to end recursion (no-op)
template<typename Expr>
auto Transform(const Expr& expr) -> Expr
{
return expr;
}
template<typename LeftLeft, typename LeftRight, typename Right>
auto Transform(BinaryTimesExpr < BinaryTimesExpr<LeftLeft, LeftRight>,
Right > const& expr)
-> decltype(Transform(
BinaryTimesExpr < LeftLeft,
BinaryTimesExpr<LeftRight, Right>
> {expr.left.left, {expr.left.right, expr.right}}
))
{
return Transform(
BinaryTimesExpr < LeftLeft,
BinaryTimesExpr<LeftRight, Right>
> {expr.left.left, {expr.left.right, expr.right}}
);
}
int main()
{
BinaryTimesExpr<int, int> beg{1,2};
auto res = beg*3*4*5*6;
std::cout << res << std::endl;
std::cout << Transform(res) << std::endl;
}
输出:
(((((1 * 2)* 3)* 4)* 5)* 6)
(1 *(2 *(3 *(4 *(5 * 6)))))
答案 2 :(得分:0)
表达式实际上是二叉树。例如,表达式a * b * c * d * e
被评估为((((a * b) * c) * d) * e)
,所以你所拥有的就是下面的树(对于三岁孩子般的绘图,没有手写笔的ipad抱歉......): / p>
正如您所看到的,生成默认的计算表达式是使用左侧优先顺序来遍历树。
另一方面,使用右侧优先顺序生成反向评估的表达式(什么OP想要)?
因此,一种解决方案是在右侧第一顺序中遍历生成的表达式树,并在该过程中创建一个新树。