C++ 自定义新的运算符

268 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情

在C++中,我们可以对已有运算符进行重载,但是我们却不能新增加运算符~
可是,我们可以通过已有运算符和运算符重载来达到假装添加新的运算符的效果。

效果展示

std::cout << (a <add> b) << "\n"
          << (a <sub> b) << "\n"
          << (a <mlt> b) << "\n"
          << (a <div> b) << "\n";

我们现在要实现这样的效果,嗯,就类似于 <oper> 以尖括号括起来的就是我们的运算符。

如何实现这种操作呢?

其实是通过重载 大于小于号来完成的

把原来的算式拆开就是:

(lsh < oper) > rsh

就是说,其实是通过一种取巧的方式来达到这一目的的。

如何实现呢?

  • 重载 operator < 运算符,讲函数和左操作存储为中间变量
  • 重载 operator > 运算符,利用中间变量和右操作符进行计算

为了保持复用,可以写一个类来保存中间变量

template <typename ret> struct Operator;
template <typename ret, typename A, typename B> struct Operator<ret(A, B)> {
  A lhs;
  using func_type = std::function<ret(A, B)>;
  func_type func;
  Operator(func_type &&fun) : func(fun) {}
};

将函数绑定到类上面来,这里用了和std::function一样的技巧(特化),把返回值和参数类型确定

现在就创建好了一个 Oper 运算子

然后,重载 operator <

将左操作数存入算子对象中

template <typename ret, typename A, typename B>
Operator<ret(A, B)> operator<(A /*copy*/ a, Operator<ret(A, B)> &now) {
  now.lhs = a; // 可优化,有拷贝
  return now;
}

然后,重载 operator >

计算左操作数和右操作数的结果

template <typename ret, typename A, typename B>
ret operator>(const Operator<ret(A, B)> &now, B /*copy*/ b) {
  return now.func(now.lhs, b);
}

现在就可以简单的使用其进行算子定义了!

比如:

Operator<double(double, double)> maxs{
    [](double a, double b) -> double { return std::max(a, b); }};

然后就可以使用 a <maxs> b 这种形式来运算了

其实如果不介意使用宏的话,可以让其变得更加的 简洁

#define operator_register(name, ret, oper1, oper2, body)                       \
  Operator<ret(oper1, oper2)> name body;

#define add <o_add>
#define sub <o_sub>
#define mlt <o_mlt>
#define div <o_div>
operator_register(o_add, int, int, int,
                {[](int a, int b) -> int { return a + b; }});
operator_register(o_sub, int, int, int,
                {[](int a, int b) -> int { return a - b; }});
operator_register(o_mlt, int, int, int,
                {[](int a, int b) -> int { return a * b; }});
operator_register(o_div, int, int, int,
                {[](int a, int b) -> int { return a / b; }});
int main() {
  int a = 1, b = 1;

  
  std::cout << (a add b) << "\n"
            << (a sub b) << "\n"
            << (a mlt b) << "\n"
            << (a div b) << "\n";

  return 0;
}

当然,可以实现得更加复杂,支持运算符优先级,这是后话

现在的代码,嗯,怎么看怎么有点不想C++

std::cout << (a add b add 2 add 3) << "\n"
          << (a add b add 2 add 3 sub 4) << "\n";