Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/134.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 转换表达式模板树_C++_C++11_Metaprogramming_Expression Templates - Fatal编程技术网

C++ 转换表达式模板树

C++ 转换表达式模板树,c++,c++11,metaprogramming,expression-templates,C++,C++11,Metaprogramming,Expression Templates,给定一个表达式模板树,我想在处理它之前创建一个新的优化树。考虑乘法运算的以下示例: a * b * c * d, 由于运算符*从左到右的关联性,它生成表达式树: (((a * b) * c) * d). 我想生成一个经过转换的表达式树,其中乘法从右向左进行: (a * (b * (c * d))). 考虑二进制表达式类型: template<typename Left, typename Right> struct BinaryTimesExpr { BinaryTim

给定一个表达式模板树,我想在处理它之前创建一个新的优化树。考虑乘法运算的以下示例:

a * b * c * d,
由于
运算符*
从左到右的关联性,它生成表达式树:

(((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;
};
其中,
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;
但我不知道如何应用这个来解决我下面的问题(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;
}
请注意,除了最外部的
Transform
调用之外,有必要对每个子表达式应用
Transform
函数(请参阅上一个
Transform
重载)


可以找到完整的源代码。

不包含完美转发:

#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;
}
#包括
//通过使其成为聚合而简化
模板
结构二进制时间表达式
{
左-左;
对对,;
};
//“调试”/“演示输出”
模板

std::ostream&operator表达式实际上是一个二叉树。例如,表达式
a*b*c*d*e
的计算结果为
((((a*b)*c)*d)*e)
,因此您拥有的是以下树(很抱歉,三岁的儿童画,ipad没有触针……):

如您所见,默认的计算表达式是按照树的左侧优先顺序生成的

另一方面,反向计算表达式(OP想要的)是在树中以右侧第一的顺序生成的:


因此,一种解决方案是按顺序在右侧首先遍历生成的表达式树,并在此过程中创建一个新的树。

尽管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) );
}

您是否考虑过使用Boost.Proto来帮助解决这个问题?Cpp Next目前似乎处于低迷状态,但他写了一篇关于使用Proto对数学表达式重新排序的文章。他的例子是(A+B)[2]到A[2]+B[2],但由于它们都只是运算符,所以应该应用。这将集成到一个库中,不需要第三方依赖项。1)Boost.Proto仅为标头。2) 您希望使用元编程转换器将
((a*b)*c*d)
转换为
(a*(b*(c*d))
,还是将表达式树直接构建为
(a*(b*(c*d))
?@Manu343726我相信DyP是正确的。我需要递归来完全解决这个问题。此外,当我们有矩阵乘法时,顺序也很重要。@Allan顺序对浮点类型也很重要。在编译这段代码时,我遇到了一个奇怪的编译错误:内部编译器错误:重新输入错误报告例程。无论如何,我相信在第二次重载时,需要额外调用
Transform
。请尝试打印(2*3)*(4*5)以查看是否生成了(2*(3*(4*5))。@Allan o.o我正在使用clang++3.4@Allan Yes此示例不折叠,它只是“反转”树。需要在
expr.left.left
上执行
转换
调用,
expr.left.right
expr.right
@Allan我将首先尝试找出如何整合完美的转发。将
Transform
调用添加到其他两个部分非常简单。树是什么更复杂(涉及更多分支):(2*3)*(4*(5*6))?@Allan首先请注意,我对((a*b)*c)的意思是表达式的形式(如何计算)
a*b*c
,这就是编译器根据运算符优先级规则计算表达式的方式。不是给定表达式(如
((a*b)*c)
)生成的树。
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))))))
#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;
}
#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)
        )
    )
)