C ++ мета-функция над шаблонами - PullRequest
0 голосов
/ 23 февраля 2020

У меня есть несколько шаблонов, подобных приведенным ниже, которые я могу использовать для определения простых выражений, например

Expr<constant,int,int,1,1> = 2

Expr<sub, Expr<constant,int,int,1,1>, Expr<constant,int,int,2,0>, 1, 1> = x - 2.

Я хочу определить мета-функцию, которая принимает Expr и возвращает другой Expr, который является модифицированной версией той, которая передается в качестве входных данных. Выходные данные будут основаны на аргументах шаблона входных данных, поэтому я должен определить несколько шаблонов функций, которые специализируются на различных входных данных. В конечном итоге моя цель состоит в том, чтобы иметь возможность различать Expr.

// the types of expressions (+,-,*, etc.)
enum ExprType { mul, divide, add, sub, constant};

// constant
template <ExprType eType, class Left, class Right, int coeff, int power> struct Expr {
    static double eval(double x){
        return coeff * std::pow(x, power);
    }
};

//sub
template <class Left, class Right, int coeff, int power> struct Expr<sub, Left, Right, coeff, power> {
    static double eval(double x){
        return coeff * std::pow(Left::eval(x) - Right::eval(x), power);
    }
};

// add
template <class Left, class Right, int coeff, int power> struct Expr<add, Left, Right, coeff, power> {
    static double eval(double x){
        return coeff * std::pow(Left::eval(x) + Right::eval(x), power);
    }
};

Однако у меня возникают проблемы, когда я оборачиваюсь вокруг определения функции. Пока у меня есть:

template <template <ExprType eType, class Left, class Right, int coeff, int power> class E> struct ExprDerivative {
    static E derivative(E e){
        return e;
    }
};

Я иду в правильном направлении? Как определить мета-функцию над шаблонами?

Ответы [ 2 ]

1 голос
/ 23 февраля 2020

Вам нужна черта типа, которая является функцией с типами в качестве аргументов. Определение черты типа не имеет ничего общего с функцией значений (по сути, они написаны в функциональном стиле программирования с «уравнениями»), но они называются так, как вы и ожидаете (func<args>).

template<typename Differentiand> struct derivative;
// shorthand for calling the function: derivative_t<expr...>
template<typename Differentiand>
using derivative_t = typename derivative<Differentiand>::result;

// every "equation" is a specialization of derivative for a certain set of Exprs that defines the result as a member type
template<typename L, typename R, int coeff, int power>
struct derivative<Expr<constant, L, R, coeff, power>> { using result = Expr<constant, L, R, coeff*power, power - 1> };
// etc

Однако меня беспокоит то, как вы написали тип Expr. constant s не являются константами; это выражения вида cx^n. Также у них есть посторонние левый и правый операнды. Было бы лучше сделать это

struct variable {
    static constexpr double eval(double x) { return x; }
};
template<int Value>
struct constant {
    static constexpr double eval(double x) { return Value; } 
};
template<typename Left, typename Right>
struct addition {
    static constexpr double eval(double x) { return Left::eval(x) + Right::eval(x); }
};
template<typename Left, typename Right>
struct multiplication {
    static constexpr double eval(double x) { return Left::eval(x) * Right::eval(x); }
};
template<typename Base, int Power>
struct exponentiation {
    static double eval(double x) { return std::pow(Base::eval(x), Power); }
};
// no need to add these as "primitives"
template<typename Left, typename Right>
using subtraction = addition<Left, multiplication<constant<-1>, Right>>;
template<typename Left, typename Right>
using division = multiplication<Left, exponentiation<Right, -1>>;

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

template<>
struct derivative<variable> { using result = constant<1>; };
template<int Value>
struct derivative<constant<Value>> { using result = constant<0>; };
template<typename L, typename R>
struct derivative<addition<L, R>> { using result = addition<derivative_t<L>, derivative_t<R>>; };
template<typename L, typename R>
struct derivative<multiplication<L, R>> { using result = addition<multiplication<derivative_t<L>, R>, multiplication<L, derivative_t<R>>>; };
template<typename B, int N>
struct derivative<exponentiation<B, N>> { using result = multiplication<multiplication<constant<N>, exponentiation<B, N - 1>>, derivative_t<B>>; };

Например

int main() {
    // y = (x^2 + 1)/x
    // dy/dx = 1 - x^-2
    // dy/dx(x = 2) = 1 - 1/4 = 0.75
    std::cout << derivative_t<division<addition<exponentiation<variable, 2>, constant<1>>, variable>>::eval(2) << "\n";
}
0 голосов
/ 23 февраля 2020

Не уверен, что понимаю, что вы хотите ... но уверен, что вы не можете передать аргумент шаблона-шаблона в качестве аргумента функции.

Мне кажется, что ваш функционал ExprDerivative() может быть написан, как функция шаблона, которая позволяет выводить шаблон-шаблона и параметры шаблона из аргумента e следующим образом

template <template <ExprType, typename, typename, int, int> class E,
          ExprType eType, typename Left, typename Right,
          int coeff, int power>
auto ExprDerivative (E<eType, Left, Right, coeff, power> e)
 { return e; }

Обратите внимание, что таким образом аргумент e имеет тип E<eType, Left, Right, coeff, power> не типа E (это не тип).

Вы можете использовать его, например, следующим образом

Expr<constant, int, int, 1, 1> e0;

auto e1 = ExprDerivative(e0);
...