Это базовая реализация, которую я использую для шаблонов выражений , основанная на CRTP, которая позволяет мне удобно комбинировать несколько типов операций, не запрашивая при этом everything как выражениеtree.
template<typename E>
struct OpExpression
{
auto eval(iter_type n) const
{
return static_cast<E const&>(*this).eval(n);
}
};
template<typename T>
struct OpFrame : OpExpression<OpFrame<T>>
{
T *ptr;
OpFrame(T *ptr) : ptr{ ptr } {}
T eval(iter_type n) const
{
return ptr[n];
}
};
template<typename T>
struct OpLiteral : OpExpression<OpLiteral<T>>
{
T value;
OpLiteral<T>(T value) : value{ value } {}
T eval(iter_type) const
{
return value;
}
operator T() const
{
return value;
}
};
Вот класс, чтобы применить сложение между двумя значениями (и перегрузку оператора, чтобы сделать его симпатичным):
template<typename E1, typename E2>
struct OpFrameAdd : OpExpression<OpFrameAdd<E1, E2>>
{
OpFrameAdd(E1 const a, E2 const b) : a{ a }, b{ b } {}
auto eval(iter_type n) const
{
return a.eval(n) + b.eval(n);
}
protected:
E1 const a;
E2 const b;
};
template<typename E1, typename E2>
auto operator+(OpExpression<E1> const& a, OpExpression<E2> const& b)
{
auto v = OpFrameAdd<E1, E2>(*static_cast<const E1*>(&a), *static_cast<const E2*>(&b));
return v;
}
Чтобы предоставить больше деталей / контекста;У меня есть массив, содержащий кучу значений (может быть разных типов), и арифметическое выражение, определяющее, как я хочу преобразовать этот массив в другой.Однако это преобразование не является конкретным, поэтому я использую дерево выражений как нечто, что я могу передать объектам, которые затем будут обрабатывать его различными способами (т.е. мне нужно ждать, чтобы оценить его).Кроме того, я хочу определить выражение только один раз.
Мне интересно, если на основе этой схемы я могу ввести литералы ( без приведения к OpLiteral
) в мои выражения?Например:
double arr[100]{ 0 };
OpFrame arr_t(arr);
// I can do this
auto ev1 = arr_t + arr_t + arr_t + OpLiteral(2.0);
// But I would prefer to do this
auto ev2 = arr_t + arr_t + arr_t + 2.0;
Исходя из моего вопроса здесь , я знаю, 2.0
не будет автоматически приведен к правильному типу, но решение также не совместимо с этим дизайном (это вызывает либо неоднозначный вызов, либо в больших выражениях, смешивает дерево, применяя общий шаблон, а не шаблон на основе OpExpression<T>
).
Как я пытался реализовать это решение:
template<typename E1, typename T>
auto operator+(OpExpression<E1> const& a, T b)
{
auto v = OpFrameAdd<E1, OpLiteral<T>>(*static_cast<const E1*>(&a), OpLiteral<T>(b));
return v;
}
template<typename E2, typename T>
auto operator+(T a, OpExpression<E2> const& b)
{
auto v = OpFrameAdd<OpLiteral<T>, E2>(OpLiteral<T>(a), *static_cast<const E2*>(&b));
return v;
}
Итак, мои вопросы:
Можно ли расширить дизайн, чтобы использовать литерал предпочтительным образом?Если нет, то это только ограничение шаблонов / дизайна, которое я выбрал?(Приведение к OpLiteral
все еще намного проще, чем перегрузка нового оператора для каждого типа и для обеих сторон).В более широком смысле, есть ли известный (другой) дизайн для решения этой проблемы?Правильно ли я применил этот дизайн к проблеме?
РЕДАКТИРОВАТЬ
При данной конструкции не представляется возможным принять и преобразовать другой тип неявным образом.В конечном счете, кажется, что проблему можно сформулировать так: я хочу выполнить операцию между 1) родительским классом и другим родительским классом;2) родительский класс - любой другой объект;3) Любой класс и тот родительский класс.Очевидно, это по своей сути проблематично.
Например, когда я попытался исправить неоднозначный вызов в приведенной выше попытке, вместо этого это приводит к тому, что операции между дочерними элементами OpExpression
становятся аргументом шаблона OpLiteral
.Это связано с тем, что вместо разрешения «правильного» оператора (т. Е. Применения оператора с обоими типами аргументов OpExpression
) он выберет оператор с более общим типом аргумента.
Вывод таков:это просто ограничение дизайна (и по уважительной причине).
Однако предположим, что у меня есть полный список всех литеральных типов, которые я хотел бы использовать в выражении.Если я не хочу создавать отдельные шаблоны специализаций для каждой из них, как бы я изменил перегруженный оператор, чтобы использовать это?Должен ли я вместо этого изменить классы OpExpression
?