Можно ли создать пользовательский оператор, чтобы вы могли делать такие вещи?
if ("Hello, world!" contains "Hello") ...
Примечание: это отдельный вопрос: "Это хорошая идея...";)
Можно ли создать пользовательский оператор, чтобы вы могли делать такие вещи?
if ("Hello, world!" contains "Hello") ...
Примечание: это отдельный вопрос: "Это хорошая идея...";)
Есть несколько общедоступных инструментов, которые помогут вам. Оба используют генерацию кода препроцессора для создания шаблонов, которые реализуют пользовательские операторы. Эти операторы состоят из одного или нескольких встроенных операторов в сочетании с идентификатором.
Так как это не обычные пользовательские операторы, а просто трюки перегрузки оператора, есть несколько предостережений:
_
, o
или аналогично простые буквенно-цифровые символы.Пока я работал над своей собственной библиотекой для этой цели (см. ниже), я столкнулся с этим проектом. Ниже приведен пример создания оператора avg
:
#define avg BinaryOperatorDefinition(_op_avg, /)
DeclareBinaryOperator(_op_avg)
DeclareOperatorLeftType(_op_avg, /, double);
inline double _op_avg(double l, double r)
{
return (l + r) / 2;
}
BindBinaryOperator(double, _op_avg, /, double, double)
Что началось с упражнение в чистой легкомыслие, стало моим собственным решением этой проблемы. Здесь аналогичный пример:
template<typename T> class AvgOp {
public:
T operator()(const T& left, const T& right)
{
return (left + right) / 2;
}
};
IDOP_CREATE_LEFT_HANDED(<, _avg_, >, AvgOp)
#define avg <_avg_>
В "Синтаксическом аспартаме" Сандера Стокса подробно рассмотрен метод, позволяющий использовать следующий формат:
if ("Hello, world!" <contains> "Hello") ...
По сути, вам нужен прокси-объект с перегруженными операторами "<" и ">". Прокси выполняет всю работу; "содержит" может быть просто синглтон без собственного поведения или данных.
// Not my code!
const struct contains_ {} contains;
template <typename T>
struct ContainsProxy
{
ContainsProxy(const T& t): t_(t) {}
const T& t_;
};
template <typename T>
ContainsProxy<T> operator<(const T& lhs, const contains_& rhs)
{
return ContainsProxy<T>(lhs);
}
bool operator>(const ContainsProxy<Rect>& lhs, const Rect& rhs)
{
return lhs.t_.left <= rhs.left &&
lhs.t_.top <= rhs.top &&
lhs.t_.right >= rhs.right &&
lhs.t_.bottom >= rhs.bottom;
}
Чтобы быть более точным, сам С++ поддерживает только создание новых перегрузок существующих операций, а не создание новых операторов. Существуют языки (например, ML и большинство его потомков), которые позволяют создавать совершенно новые операторы, но С++ не является одним из них.
Из взглядов вещей (по крайней мере) библиотека CustomOperators, упомянутая в другом ответе, также не поддерживает полностью настраиваемые операторы. По крайней мере, если я правильно читаю вещи, он (внутренне) переводит ваш пользовательский оператор в перегрузку существующего оператора. Это упрощает работу за счет некоторой гибкости - например, когда вы создаете новый оператор в ML, вы можете придать ему приоритет, отличный от функции любого встроенного оператора.
Ваше предложение было бы не более чем синтаксическим сахаром для:
if( contains( "Hello, world!", "Hello" ) ...
и на самом деле уже есть функции для выполнения как в cstring, так и в std::string. Возможно, это немного похоже на ответ: "Это хорошая идея?" но не совсем; скорее спрашивая: "зачем вам это нужно?"
Технически, нет. То есть вы не можете расширять набор operator+
, operator-
и т.д. Но то, что вы предлагаете в своем примере, - это что-то еще. Вам интересно, существует ли определение "содержит" такое, что string-literal "contains" string-literal
является выражением с нетривиальной логикой (#define contains ""
является тривиальным случаем).
Существует не так много выражений, которые могут иметь форму string-literal X string-literal
. Это связано с тем, что сами строковые литералы являются выражениями. Итак, вы ищете языковое правило формы expr X expr
. Их довольно много, но для операторов все правила, и они не работают на строках. Несмотря на очевидную реализацию, "Hello, " + "world"
не является допустимым выражением. Итак, что еще может быть X в string-literal X string-literal
? Это не может быть само выражение. Это не может быть имя типа, имя typedef или имя шаблона. Это не может быть имя функции. На самом деле это может быть только макрос, который является единственными оставшимися именованными объектами. Для этого см. Ответ "Да (ну, вид)".
Я создал следующие два макроса:
#define define const struct
#define operator(ReturnType, OperatorName, FirstOperandType, SecondOperandType) OperatorName ## _ {} OperatorName; template <typename T> struct OperatorName ## Proxy{public:OperatorName ## Proxy(const T& t) : t_(t){}const T& t_;static ReturnType _ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b);};template <typename T> OperatorName ## Proxy<T> operator<(const T& lhs, const OperatorName ## _& rhs){return OperatorName ## Proxy<T>(lhs);}ReturnType operator>(const OperatorName ## Proxy<FirstOperandType>& lhs, const SecondOperandType& rhs){return OperatorName ## Proxy<FirstOperandType>::_ ## OperatorName ## _(lhs.t_, rhs);}template <typename T> inline ReturnType OperatorName ## Proxy<T>::_ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b)
Затем вам нужно просто определить свой пользовательский оператор, как в следующем примере:
define operator(bool, myOr, bool, bool) { // Arguments are the return type, the name of the operator, the left operand type and the right operand type, respectively
return a || b;
}
#define myOr <myOr> // Finally, you have to define a macro to avoid to put the < and > operator at the start and end of the operator name
После того, как вы настроили своего оператора, вы можете использовать его в качестве предопределенного оператора:
bool a = true myOr false;
// a == true