В 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()&&
вернуть выведенный тип. Если ваш компилятор не поддерживает эту функцию, =equals=
может вернуть правильный тип с умеренной ценой ясности.
Обратите внимание, что приведенный выше синтаксис на самом деле реализуется в C ++ с использованием методов, аналогичных операциям. Фактическая реализация больше, чем должна содержать публикация SO.
Есть и другие преимущества. Как всем известно, непонятные символы ASCII в языках программирования перестали нравиться, и люди, читающие C ++, могут быть убеждены такими выражениями, как:
int z = (a + b* pow(c,pow(x,a+b))*y);
С помощью этой техники все операторы имеют читаемые имена, которые проясняют их значение, и все делается инфиксно, а не смешивает инфиксную и префиксную нотацию.
Подобные решения, чтобы гарантировать, что pow
доступно может быть сделано путем переопределения <cmath>
как <cmath_nopow>
сам. Это позволяет избежать перегрузки оператора не в языковых конструкциях, что приводит к разделению грамматических монад AST и / или нарушению стандарта. Может быть, попробуйте Haskell?
Других решений пока нет …