你可以在 C++ 中制作自定义运算符吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1515399/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:14:17  来源:igfitidea点击:

Can you make custom operators in C++?

c++templatesoperatorsc-preprocessormetaprogramming

提问by Cogwheel

Is it possible to make a custom operator so you can do things like this?

是否可以制作自定义运算符以便您可以执行此类操作?

if ("Hello, world!" contains "Hello") ...

Note: this is a separate question from "Is it a good idea to..." ;)

注意:这是一个独立于“这是一个好主意......”的问题;)

采纳答案by Cogwheel

Yes! (well, sort of)

是的!(嗯,有点)

There are a couple publicly available tools to help you out. Both use preprocessor code generation to create templates which implement the custom operators. These operators consist of one or more built-in operators in conjunction with an identifier.

有一些公开可用的工具可以帮助您。两者都使用预处理器代码生成来创建实现自定义运算符的模板。这些运算符由一个或多个内置运算符与标识符组成。

Since these aren't actually custom operators, but merely tricks of operator overloading, there are a few caveats:

由于这些实际上不是自定义运算符,而只是运算符重载的技巧,因此有一些警告:

  • Macros are evil. If you make a mistake, the compiler will be all but entirely useless for tracking down the problem.
  • Even if you get the macro right, if there is an error in your usage of the operator or in the definition of your operation, the compiler will be only slightly more helpful.
  • You must use a valid identifier as part of the operator. If you want a more symbol-like operator, you can use _, oor similarly simple alphanumerics.
  • 宏是邪恶的。如果你犯了一个错误,编译器对于追踪问题几乎毫无用处。
  • 即使您得到了正确的宏,如果您在使用运算符或在您的操作定义中出现错误,编译器也只会稍微提供一些帮助。
  • 您必须使用有效标识符作为运算符的一部分。如果您想要更像符号的运算符,可以使用_o或类似的简单字母数字。

CustomOperators

自定义运算符

While I was working on my own library for this purpose (see below) I came across this project. Here is an example of creating an avgoperator:

当我为此目的开发自己的库时(见下文),我遇到了这个项目。以下是创建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)

IdOp

运营商

What started as an exercise in pure frivolitybecame my own take on this problem. Here's a similar example:

一开始纯粹是无聊的练习,后来变成了我自己对这个问题的看法。下面是一个类似的例子:

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_>

Key Differences

主要区别

  • CustomOperators supports postfix unary operators
  • IdOp templates use references rather than pointers to eliminate use of the free store, and to allow full compile-time evaluation of the operation
  • IdOp allows you to easily specify several operations for the same root identifier
  • CustomOperators 支持后缀一元运算符
  • IdOp 模板使用引用而不是指针来消除对免费存储的使用,并允许对操作进行完整的编译时评估
  • IdOp 允许您轻松地为同一个根标识符指定多个操作

回答by John P

There's a method thoroughly explored in 'Syntactic Aspartame'by Sander Stoks that would allow you to use the following format:

Sander Stoks在“Syntactic Aspartame”中彻底探索了一种方法,它允许您使用以下格式:

if ("Hello, world!" <contains> "Hello") ...

In essence, you need a proxy object with the operators '<' and '>' overloaded. The proxy does all of the work; 'contains' can just be a singleton with no behavior or data of its own.

本质上,您需要一个重载运算符“<”和“>”的代理对象。代理完成所有工作;“包含”可以只是一个没有行为或数据的单身人士。

// 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;
}

回答by Jerry Coffin

To be a bit more accurate, C++ itselfonly supports creating new overloads of existing operations, NOT creating new operators. There are languages (e.g., ML and most of its descendants) that do allow you to create entirely new operators, but C++ is not one of them.

更准确地说,C++本身仅支持创建现有操作的新重载,而不支持创建新的运算符。有些语言(例如 ML 及其大多数后代)确实允许您创建全新的运算符,但 C++ 不是其中之一。

From the looks of things, (at least) the CustomOperators library mentioned in the other answer doesn't support entirely custom operators either. At least if I'm reading things correctly, it's (internally) translating your custom operator into an overload of an existing operator. That makes things easier, at the expense of some flexibility -- for example, when you create a new operator in ML, you can give it precedence different from that of any built-in operator.

从表面上看,(至少)另一个答案中提到的 CustomOperators 库也不完全支持自定义运算符。至少如果我正确阅读,它(在内部)将您的自定义运算符转换为现有运算符的重载。这让事情变得更容易,但牺牲了一些灵活性——例如,当你在 ML 中创建一个新的运算符时,你可以赋予它不同于任何内置运算符的优先级。

回答by Davide Cannizzo

I've created the following two macros:

我创建了以下两个宏:

#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)

Then, you'd have just to define your custom operator as in the following example:

然后,您只需定义您的自定义运算符,如下例所示:

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

Once a time you've set your operator up, you can use it as a predefined operator:

一旦您设置了运算符,您就可以将其用作预定义的运算符:

bool a = true myOr false;
// a == true

回答by MSalters

Technically, no. That is to say, you can't extend the set of operator+, operator-, etcetera. But what you're proposing in your example is something else. You are wondering if there is a definition of "contains" such that string-literal "contains" string-literalis an expression, with non-trivial logic (#define contains ""being the trivial case).

从技术上讲,没有。也就是说,您不能扩展operator+operator-、 等的集合。但是你在你的例子中提出的是另一回事。你想知道是否有一个“包含”的定义,这样string-literal "contains" string-literal一个表达式,具有非平凡的逻辑(#define contains ""是平凡的情况)。

There are not many expressions that can have the form string-literal X string-literal. This is because string literals themselves are expressions. So, you're looking for a language rule of the form expr X expr. There are quite a few of those, but they're all rules for operators, and those don't work on strings. Despite the obvious implementation, "Hello, " + "world"is not a valid expression. So, what else can X be in string-literal X string-literal? It can't be a expression itself. It can't be a typename, a typedef name or a template name. It can't be a function name. It can really only be a macro, which are the only remaining named entities. For that, see the "Yes (well, sort of)" answer.

可以具有这种形式的表达式并不多string-literal X string-literal。这是因为字符串文字本身就是表达式。因此,您正在寻找形式为 的语言规则expr X expr。其中有很多,但它们都是运算符的规则,并且不适用于字符串。尽管有明显的实现,但"Hello, " + "world"不是有效的表达式。那么,X 还能做什么string-literal X string-literal呢?它不能是一个表达式本身。它不能是类型名称、类型定义名称或模板名称。它不能是函数名。它实际上只能是一个宏,它是唯一剩下的命名实体。为此,请参阅“是(好吧,有点)”的答案。

回答by Clifford

Your suggestion would be nothing more than syntactic sugar for:

您的建议只不过是以下方面的语法糖:

if( contains( "Hello, world!", "Hello" ) ...

and in fact there are already a functions to do that in both cstring and std::string. Which is perhaps a bit like answering "is it a good idea?" but not quite; rather asking "why would you need/want to?"

事实上,在 cstring 和 std::string 中已经有一个函数可以做到这一点。这可能有点像回答“这是个好主意吗?” 但不完全是;而不是问“你为什么需要/想要?”