用户定义的中缀运算符

User-defined infix operators

本文关键字:运算符 中缀 定义 用户      更新时间:2023-10-16

在C++中引入新的中缀运算符很容易

// User-defined infix operator framework
template <typename LeftOperand, typename Operation>
struct LeftHelper
{
    const LeftOperand& leftOperand;
    const Operation& operation;
    LeftHelper(const LeftOperand& leftOperand, 
               const Operation& operation)
        : leftOperand(leftOperand), operation(operation) {}
};
template <typename LeftOperand, typename Operation >
auto operator < (const LeftOperand& leftOperand, 
                 Operation& operation)
{
    return LeftHelper<LeftOperand, Operation>(leftOperand, operation);
}
template <typename LeftOperand, typename Operation, typename RightOperand>
auto operator > (LeftHelper<LeftOperand, Operation> leftHelper, 
                 const RightOperand& rightOperand)
{
    return leftHelper.operation(leftHelper.leftOperand, rightOperand);
}
// Defining a new operator
#include <cmath>
static auto pwr = [](const auto& operand1, const auto& operand2) { return std::pow(operand1, operand2); };
// using it
#include <iostream>
int main() 
{
   std::cout << (2 <pwr> 16) << std::endl;
   return 0;
}

现场演示

不幸的是,这个幂运算符具有错误的优先级和关联性。所以我的问题是:如何解决这个问题我希望我的<pow>具有比*更高的优先级,并关联到右侧,就像在数学符号中一样。

编辑可以通过使用不同的括号来改变优先级,例如|op|/op/*op*,甚至如果有人倾向于<<--op-->>,也可以改变优先级,但这样不能高于最高内置运算符优先级。但如今C++在模板元编程和类型推导方面非常强大,应该有其他方法来实现所需的结果。

此外,如果我可以使用pow而不是pwr,那就太好了。不幸的是,在某些实现中,#include <cmath>pow带入全局命名空间,因此会发生冲突。我们是否可以重载operator not,以使形式的声明

not using std::pow;

是否从全局命名空间中删除了std::pow

进一步阅读:Bjarne Stroustrup的相关提案。

最小惊奇原则很重要,a*b *power* c * da* (b^c) *d的评价是关键。幸运的是,有一个简单的解决方案。

为了确保*power*的优先级高于乘法,您必须使用类似的命名运算符技术进行乘法运算。

然后,您不直接计算*power**times*的结果,而是构建一个表达式树。此表达式树在求值时可以应用任意优先级规则

我们可以使用每个内置运算符来实现这一点,为我们提供了一种易于阅读的语法,允许在编译时对运算符优先级进行元编程:

auto z =equals= bracket<
  a *plus* b *times* c *power* bracket<
    a *plus* b
  >bracket *power* x *times* y
>bracket;

为了避免此表达式模板的存储时间超过最佳值,只需重载operator auto()&&即可返回推导出的类型。如果编译器无法支持该功能,=equals=可以以较低的清晰度返回正确的类型。

请注意,上述语法实际上可以在C++中使用类似于OP的技术来实现。实际的实现比SO帖子应该包含的要大。

还有其他好处。众所周知,编程语言中晦涩难懂的ASCII字符已经失宠,阅读C++的人可能会被以下表达式所困扰:

int z = (a + b* pow(c,pow(x,a+b))*y);

使用这种技术,所有运算符都有可读的名称,使其含义清晰明了,并且所有操作都是中缀完成的,而不是混合中缀和前缀表示法。

确保pow可用的类似解决方案可以通过将<cmath>重新实现为<cmath_nopow>来实现。这避免了重载运算符而不是在语言结构上,这会导致AST语法单元解耦和/或违反标准。也许试试哈斯克尔?