4

我有一个递归类Expression,它表示类似布尔的表达式,例如:

(a & b) | (c & ~d)

请注意,Expression它同时处理一元和二元表达式。基本上,Expression应该遵循类似于布尔表达式的CFG。

我以这种方式设计了课程:

class Expression {
public:
    Expression() = default;
    Expression(unique_ptr<Expression> lhs, unique_ptr<Expression> rhs,
        unique_ptr<IBinaryOperator> binop, unique_ptr<IUnaryOperator> unop);
    Expression operator^(Expression& that);
    Expression operator%(Expression& that);
    Expression operator|(Expression& that);
    Expression operator*(Expression& that);
    Expression operator+(Expression& that);
    Expression operator&(Expression& that);
    Expression operator>>(Expression& that);
    Expression operator!();
    Expression operator~();
    double Evaluate(double x);
    virtual ~Expression();
protected:
    unique_ptr<Expression> _lhs = nullptr;
    unique_ptr<Expression> _rhs = nullptr;
    unique_ptr<IBinaryOperator> _binop = nullptr;
    unique_ptr<IUnaryOperator> _unop = nullptr;
};

构造函数的实现以及二元和一元运算符各一个如下所示:

Expression::Expression(unique_ptr<Expression> lhs, unique_ptr<Expression> rhs, unique_ptr<IBinaryOperator> binop, unique_ptr<IUnaryOperator> unop) :
        _lhs(move(lhs)), _rhs(move(rhs)), _binop(move(binop)), _unop(move(unop)) {
}
Expression Expression::operator+(Expression&& that) {
    return Expression(unique_ptr<Expression>(this), unique_ptr<Expression>(&that), unique_ptr<IBinaryOperator>(new SumCoNorm), nullptr);
}
Expression Expression::operator~() {
    return Expression(nullptr, unique_ptr<Expression>(this), nullptr, unique_ptr<IUnaryOperator>(new Intensify));
}

该类无法编译

error: use of deleted function 'Fuzzy::Expression::Expression(const Fuzzy::Expression&)'

在每个重载的运算符中(在 return 语句中)。我觉得某些函数在内部尝试使用不存在的 unique_ptr 的复制构造函数。我在这里和那里移动指针有什么问题吗?我将 C++11 与 GCCv4.8 一起使用。

欢迎以任何方式对类接口的更改提出建议。我宁愿避免使用原始指针。

注意:请不要建议使用解析器生成器或类似的东西,例如 Boost.Spirit、YARD 或 YACC。该应用程序要求我从头开始实现它。

4

3 回答 3

3

从概念上讲,

return Expression(...);

创建一个新Expression对象,然后将其复制或移动到返回值。在您的情况下,您没有移动构造函数(没有隐式移动构造函数,因为您有一个用户声明的析构函数)和一个已删除的复制构造函数,所以这是不可能的。

您可以使用

return {...};

避免复制/移动操作,或者你可以确保你有一个移动构造函数:

class Expression {
public:
    Expression() = default;
    Expression(Expression &&) = default;
...
};

附加说明,在 Ben Voigt 的评论之后,他正确地指出这使它可以编译,但实际上并没有工作:

unique_ptr仅适用于用 分配的对象new,除非您使用自定义删除器。在你的情况下,它只是行不通,你需要重新考虑你的逻辑。

认为你应该unique_ptr只保留一个实现细节,而不是让外部调用者担心它。如果您确保您的Expression可复制和可移动,则根据需要从内部动态分配对象Expression以存储为_lhs_rhs. 举std::vector<T>个例子,你不需要new使用它,即使出于显而易见的原因,添加足够的元素在某些时候必然会开始需要动态内存分配。

于 2014-01-20T15:53:55.230 回答
2

您的问题是您正在混合存在于自己范围内的临时对象和 a 拥有的对象unique_ptr

Expression Expression::operator+(Expression&& that) {
    return Expression(unique_ptr<Expression>(this),
                      unique_ptr<Expression>(&that),
                      unique_ptr<IBinaryOperator>(new SumCoNorm),
                      nullptr);
}

您想将一个新Expression对象作为临时对象返回,并且您拥有现有对象(通过*this)并且您拥有that. 您想获得所有权,但您不能,因此编译器会尝试创建一个副本。临时文件无论如何都会被破坏,你无法阻止它,所以你不能通过将指向它的指针放入unique_ptr.

你需要的是类似的东西

// Note: free function taking *two* operands
unique_ptr<Expression> operator+(unique_ptr<Expression> lhs,
                                 unique_ptr<Expression> rhs) {
    return unique_ptr<Expression>(
      new Expression(std::move(lhs),
                     std::move(rhs),
                     unique_ptr<IBinaryOperator>(new SumCoNorm),
                     nullptr));
}

并相应地处理最终结果。

于 2014-01-20T15:57:56.903 回答
1

第 1 步:Expression(Expression&&)=default

第 2 步:像unique_ptr<Expression>(this)becomeunique_ptr<Expression>(new Expression(std::move(*this)))unique_ptr<Expression>(&that)become之类的子句unique_ptr<Expression>(new Expression(std::move(that)))

现在,这有异常安全问题,所以你要写:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&...args) {
  return {new T(std::forward<Args>(args)...)};
}

这既使事情更安全,又让你这样做:

make_unique<Expression>(std::move(*this))
make_unique<Expression>(std::move(that))

这更简单,更安全。

接下来,您需要区分Expression&Expression&&几乎无处不在。

学习表达式模板并使用该技术可能会更好。这会让你有看起来像x + 5或类似的表达式。

于 2014-01-20T19:11:25.647 回答