c ++ 11 — перегрузка оператора C ++ в рекурсивных классах выражений

У меня рекурсивный класс 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&)'

в каждом из перегруженных операторов (в операторах возврата).
Я чувствую, что какая-то функция внутренне пытается использовать конструктор копирования unique_ptr, который не существует. Я делаю что-то не так с перемещением указателей здесь и там? Я использую C ++ 11 с GCCv4.8.

Предложения по изменению интерфейса класса любым способом приветствуются. Я бы предпочел избегать использования сырых указателей.

Заметка: Пожалуйста, не предлагайте использовать генератор синтаксического анализатора или тому подобное, например Boost.Spirit, YARD или YACC. Приложение требует от меня, чтобы реализовать это с нуля.

4

Решение

Концептуально,

return Expression(...);

создает новый Expression объект, а затем копирует или перемещает его в возвращаемое значение. В вашем случае у вас нет конструктора перемещения (не существует неявного конструктора перемещения, поскольку у вас есть деструктор, объявленный пользователем) и конструктор удаленных копий, так что это невозможно.

Ты можешь использовать

return {...};

чтобы избежать операции копирования / перемещения, или вы можете убедиться, что у вас есть конструктор перемещения:

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

Дополнительное примечание после комментариев от Бена Фойгта, который справедливо указывает, что это делает его компиляцией, но на самом деле не работает:

unique_ptr работает только для объектов, выделенных с new, если вы не используете пользовательский удалитель. В вашем случае это просто не сработает, вам нужно переосмыслить свою логику.

я считать ты должен держать unique_ptr только как деталь реализации, и не имеют внешних вызывающих абонентов беспокоиться об этом. Если вы убедитесь, что ваш Expression является копируемым и подвижным, у вас не должно быть проблем с динамическим размещением объектов по мере необходимости изнутри Expression хранить как _lhs а также _rhs, принимать std::vector<T> в качестве примера, где вам не нужно new чтобы использовать его, хотя по очевидным причинам добавление достаточного количества элементов в определенный момент обязательно начнет требовать динамического выделения памяти.

3

Другие решения

Ваша проблема в том, что вы смешиваете временные объекты, которые живут в своей области видимости, и объекты, принадлежащие 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));
}

и обработать окончательный результат соответственно.

2

Шаг 1: Expression(Expression&&)=default,

Шаг 2: пункты вроде unique_ptr<Expression>(this) становиться unique_ptr<Expression>(new Expression(std::move(*this))), а также unique_ptr<Expression>(&that) становится 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 или т.п.

1
По вопросам рекламы [email protected]