Объединение литералов с шаблонами выражений на основе CRTP - PullRequest
2 голосов
/ 23 мая 2019

Это базовая реализация, которую я использую для шаблонов выражений , основанная на 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?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...