用户定义的中缀运算符
User-defined infix operators
在 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
?
最小惊奇原则很重要,a*b *power* c * d
评估为a* (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()&&
至 return。如果您的编译器不支持该功能,=equals=
可以 return 以适度的清晰度代价获得正确的类型。
请注意,上述语法实际上可以使用类似于 OP 的技术在 C++ 中实现。实际实现比 SO post 应包含的要大。
还有其他好处。众所周知,晦涩的 ASCII 字符在编程语言中已经失宠,阅读 C++ 的人可能会被这样的表达式搞糊涂:
int z = (a + b* pow(c,pow(x,a+b))*y);
使用这种技术,所有运算符都具有可读的名称,使其含义清晰,并且一切都完成了中缀,而不是混合中缀和前缀表示法。
确保 pow
可用的类似解决方案可以通过自己将 <cmath>
重新实现为 <cmath_nopow>
来完成。这避免了重载 operator not on language constructs,这导致 AST 语法 monads 解耦,and/or 违反了标准。也许试试 Haskell?
在 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
?
最小惊奇原则很重要,a*b *power* c * d
评估为a* (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()&&
至 return。如果您的编译器不支持该功能,=equals=
可以 return 以适度的清晰度代价获得正确的类型。
请注意,上述语法实际上可以使用类似于 OP 的技术在 C++ 中实现。实际实现比 SO post 应包含的要大。
还有其他好处。众所周知,晦涩的 ASCII 字符在编程语言中已经失宠,阅读 C++ 的人可能会被这样的表达式搞糊涂:
int z = (a + b* pow(c,pow(x,a+b))*y);
使用这种技术,所有运算符都具有可读的名称,使其含义清晰,并且一切都完成了中缀,而不是混合中缀和前缀表示法。
确保 pow
可用的类似解决方案可以通过自己将 <cmath>
重新实现为 <cmath_nopow>
来完成。这避免了重载 operator not on language constructs,这导致 AST 语法 monads 解耦,and/or 违反了标准。也许试试 Haskell?