我可以在 C++ 中创建一个新的运算符吗?如何创建?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8425077/
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-28 18:33:21  来源:igfitidea点击:

Can I create a new operator in C++ and how?

c++

提问by shangping

MATLAB arrays support matrix operations and element operations. For example, M*Nand M.*N. This is a quite intuitive way to distinguish ?the two different operations. If I want to implement similar operations in C++, how can I do that?

MATLAB 数组支持矩阵运算和元素运算。例如,M*NM.*N。这是区分这两种不同操作的一种非常直观的方式。如果我想在 C++ 中实现类似的操作,我该怎么做?

Can I create a new operator, .*, too? If yes, can anyone give me some guidance?

我也可以创建一个新的运算符.*吗?如果是的话,谁能给我一些指导?

回答by Lightness Races in Orbit

No, you can't overload op.*:

不,你不能超载op.*

[C++03 & C++11: 13.5/3]:The following operators cannot be overloaded:

. .* :: ?:

[C++03 & C++11: 13.5/3]:以下运算符不能重载:

. .* :: ?:

回答by lorro

In C++, there's a list of predefined operators, most of which are overloadable (.* is not). Additionally, anyname can be used as an operator like:

在 C++ 中,有一系列预定义的运算符,其中大部分是可重载的(.* 不是)。此外,任何名称都可以用作运算符,例如:

#include <iostream>

// generic LHSlt holder
template<typename LHS, typename OP>
struct LHSlt {
    LHS lhs_;
};

// declare myop as an operator-like construct
enum { myop };

// parse 'lhs <myop' into LHSlt
template<typename LHS>
LHSlt<LHS, decltype(myop)> operator<(const LHS& lhs, decltype(myop))
{
    return { lhs };
}

// declare (int <myop> int) -> int
int operator>(LHSlt<int, decltype(myop)> lhsof, int rhs)
{
    int& lhs = lhsof.lhs_;
    // here comes your actual implementation
    return (lhs + rhs) * (lhs - rhs);
}

// strictly optional
#define MYOP <myop>

int main() {
    std::cout << (5 <myop> 2) << ' ' << (5 MYOP 2);
}

Disclaimer: This, strictly speaking, gets translated to (5 < myop) > 2, which is LHSlt<int, decltype(myop)>(5) > 2. Thus it's not a new 'operator', in C++-terms, but it's used exactly the same way, even in terms of ADL. Also, if type is large, you probably want to store const T&.

免责声明:严格来说,这被翻译成(5 < myop) > 2,也就是LHSlt<int, decltype(myop)>(5) > 2。因此,在 C++ 术语中,它不是一个新的“运算符”,但它的使用方式完全相同,即使在 ADL 方面也是如此。此外,如果类型很大,您可能想要存储const T&.

Note that you can do this with any binary operator that can be defined external to the class; precedence is based on the precedence of the two sides (<and >). Thus you can have e.g. *myop*, +myop+, <<myop>>, <myop>, |myop|in this order of precedence.

请注意,您可以使用可以在类外部定义的任何二元运算符来执行此操作;优先级基于两侧 (<>)的优先级。因此,您可以按此优先级顺序使用例如*myop*, +myop+, <<myop>>, <myop>, |myop|

If you want right-associativity, it gets a bit more tricky. You'll need both of a RHS-holder and LHS-holder (the latter being LHSlthere) and use surrounding operators such that the right one has higher precedence than the left one, e.g. a |myop> b |myop>cis a |myop> (b |myop> c). Then you need the function for both your type and your holder type as the lhs.

如果你想要右结合,它会变得有点棘手。您将需要 RHS-holder 和 LHS-holder(后者在LHSlt此处)并使用周围的运算符,以便右侧的优先级高于左侧的优先级,例如a |myop> b |myop>cis a |myop> (b |myop> c)。然后你需要你的类型和你的持有人类型的函数作为 lhs。

回答by Barry

You cannot overload .*(see Lightness' answerfor standard text), but, interestingly enough, you canoverload ->*(similar to how you can overload ->but not .). If that's sufficient for differentiation, then have at it:

您不能重载.*(请参阅Lightness对标准文本的回答),但有趣的是,您可以重载->*(类似于您可以重载->但不能重载的方式.)。如果这足以区分,那么就有:

struct Int {
    int i;

    Int operator*(Int rhs) const { return Int{i * rhs.i}; }
    Int operator->*(Int rhs) const { return Int{i + rhs.i}; }

    friend std::ostream& operator<<(std::ostream& os, Int rhs) {
        return os << "Int(" << rhs.i << ')';
    }
};

int main() {
    Int five{5};
    Int six{6};

    std::cout << (five * six) << ", " << (five ->* six) << '\n';
}

That'll print Int(30), Int(11).

那会打印Int(30), Int(11).

回答by Francis Cugler

MATLAB arrays support matrix operations and element operations. For example, M*N and M.*N. This is a quite intuitive way to distinguish ?the two different operations. If I want to implement similar operations in C++, how can I do that?

Can I create a new operator, .*, too? If yes, can anyone give me some guidance?

MATLAB 数组支持矩阵运算和元素运算。例如,M*N 和 M.*N。这是区分这两种不同操作的一种非常直观的方式。如果我想在 C++ 中实现类似的操作,我该怎么做?

我也可以创建一个新的运算符 .* 吗?如果是的话,谁能给我一些指导?

As for the first part you can overload most of the operators and there are some that you can not overload and the list of operators in C++ are:

至于第一部分,您可以重载大多数运算符,也有一些不能重载,C++ 中的运算符列表是:

  • Arithmetic

    • + (addition)
    • - (subtraction)
    • * (multiplication)
    • / (division)
    • % (modulus)
  • Bitwise

    • ^ (XOR)
    • | (OR)
    • & (AND)
    • ~ (Complement)
    • << (Shift Left, Insertion to Stream)
    • >> (Shift Right, Extraction from Stream)
  • Assignment

    • = (Assignment)
  • Relational

    • == (Equality)
    • != (Inequality)
    • > (Greater-Than)
    • < (Less-Than)
    • >= (Greater-Than Or Equal-To)
    • <= (Less-Than Or Equal-To)
  • Logical

    • ! (NOT)
    • && (AND)
    • || (OR)
  • Compound Assignment

    • += (Addition-Assignment)
    • -= (Subtraction-Assignment)
    • *= (Multiplication-Assignment)
    • /= (Division-Assignment)
    • %= (Modulus-Assignment)
    • &= (AND-Assignment)
    • |= (OR-Assignment)
    • ^= (XOR-Assignment)
    • <<= (Shift-Left Assignment)
    • >>= (Shift-Right Assignment)
  • Increment - Decrement - Both have 2 forms (prefix) and (postfix)

    • ++ (Increment)
    • -- (Decrement)
  • Subscript

    • [] (Subscript)
  • Function Call

    • () (Function Call)
  • Address, Reference, Pointer

    • operator&()
    • operator*()
    • operator->()
  • Comma

    • operator,()
  • Member Reference

    • operator->()
    • operator->*()
  • Memory Management

    • new
    • delete
    • new[]
    • delete[]
  • Conversion

    • operator "type" () const
  • NON Modifiable Operators - Operators that can not be overloaded

    • ?: (Conditional - Ternary)
    • . (Member Selection)
    • .* (Member Selection With Pointer To Member)
    • :: (Scope Resolution)
    • sizeof() (Object Size Information)
    • typeid() (Object Type Information)
  • 算术

    • + (addition)
    • - (subtraction)
    • * (multiplication)
    • / (division)
    • % (modulus)
  • 按位

    • ^ (XOR)
    • | (OR)
    • & (AND)
    • ~ (Complement)
    • << (Shift Left, Insertion to Stream)
    • >> (Shift Right, Extraction from Stream)
  • 任务

    • = (Assignment)
  • 关系型

    • == (Equality)
    • != (Inequality)
    • > (Greater-Than)
    • < (Less-Than)
    • >= (Greater-Than Or Equal-To)
    • <= (Less-Than Or Equal-To)
  • 逻辑的

    • ! (NOT)
    • && (AND)
    • || (OR)
  • 复合赋值

    • += (Addition-Assignment)
    • -= (Subtraction-Assignment)
    • *= (Multiplication-Assignment)
    • /= (Division-Assignment)
    • %= (Modulus-Assignment)
    • &= (AND-Assignment)
    • |= (OR-Assignment)
    • ^= (XOR-Assignment)
    • <<= (Shift-Left Assignment)
    • >>= (Shift-Right Assignment)
  • Increment - Decrement - 都有两种形式(前缀)和(后缀)

    • ++ (Increment)
    • -- (Decrement)
  • 下标

    • [] (Subscript)
  • 函数调用

    • () (Function Call)
  • 地址、引用、指针

    • operator&()
    • operator*()
    • operator->()
  • 逗号

    • operator,()
  • 会员参考

    • operator->()
    • operator->*()
  • 内存管理

    • new
    • delete
    • new[]
    • delete[]
  • 转换

    • operator "type" () const
  • NON Modifiable Operators - 不能重载的运算符

    • ?: (Conditional - Ternary)
    • . (Member Selection)
    • .* (Member Selection With Pointer To Member)
    • :: (Scope Resolution)
    • sizeof() (Object Size Information)
    • typeid() (Object Type Information)

So knowing this list will help to answer your questions. Can you Create a "New Operator" in C++? No! If you want to implement similar operations in C++; how can I do that?

因此,了解此列表将有助于回答您的问题。你能在 C++ 中创建一个“新运算符”吗?不!如果你想在C++中实现类似的操作;我怎样才能做到这一点?

You have 4 choices: Either overload an already existing operator that can be overloaded, write a function or method to do the type of calculations you want to perform, create a template type to do the work for you, or the last one which is the least common to do but you can also write macros to do them for you.

你有 4 种选择:重载一个已经存在的可以重载的运算符,编写一个函数或方法来执行你想要执行的计算类型,创建一个模板类型来为你完成工作,或者最后一个是最不常见的事情,但您也可以编写宏来为您做这些事情。

There is a header only Math API Library that is used quite frequently with OpenGL graphics API and OpenGL's Shader Language GLSL and this library has many features that work with vectors, matrices, quaternions etc., and all the necessary functions and operations that can be done to them. Here is the link to GLMYou can have a look at their documentation as well as their library implementations since it is a headers only library or API. This should give you some insight on how they constructed their Vector and Matrix objects and the operations that can be done to them.

有一个只有头文件的 Math API 库,它经常与 OpenGL 图形 API 和 OpenGL 的着色器语言 GLSL 一起使用,该库具有许多处理向量、矩阵、四元数等的功能,以及可以完成的所有必要功能和操作给他们。这是GLM的链接您可以查看他们的文档以及他们的库实现,因为它是一个只有头文件的库或 API。这应该能让您深入了解他们如何构造 Vector 和 Matrix 对象以及可以对它们执行的操作。

回答by matthias

No, unfortunately you cannot define new operators—you can only overload existing operators (with a few important exceptions, such as operator.). Even then, it's typically only a good idea to overload operators for types which have very clear and uncontroversial existing semantics for a given operator—for instance, any type that behaves as a number is a good candidate for overloading the arithmetic and comparison operators, but you should make sure that operator+doesn't, say, subtract two numbers.

不,不幸的是您不能定义新的运算符——您只能重载现有的运算符(除了一些重要的例外,例如operator.)。即便如此,对于给定运算符具有非常清晰且无争议的现有语义的类型,重载运算符通常只是一个好主意——例如,任何表现为数字的类型都是重载算术和比较运算符的好候选,但是你应该确保它operator+不会减去两个数字。

回答by Peter

BTW: I am seeking to answer the parts of this question as asked. I am also not seeking to replicate all the information in other worthy answers. The bounty seeks something different to the question as asked, so I am not responding to that.

顺便说一句:我正在寻求回答这个问题的部分内容。我也不想在其他有价值的答案中复制所有信息。赏金寻求与所问问题不同的东西,所以我没有对此做出回应。

It is actually fairly simple to provide a matrix multiplication. Since I'm not proposing to describe data structures to represent a matrix and fully implement operations and validity checks on them, I'll just provide skeletons to illustrate.

提供矩阵乘法实际上相当简单。由于我不打算描述数据结构来表示矩阵并完全实现对它们的操作和有效性检查,因此我将仅提供骨架来说明。

Example 1: operator*()as a member function

示例 1: operator*()作为成员函数

class M   // a basic matrix class
{
    public:

          // assume other constructors and members to set things up
       M operator*(const M &rhs) const;
};

M M::operator*(const M &rhs) const
{
       //   implement checks on dimensions, throw an exception if invalid

       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

int main()
{
     M a;
     M b;
        // set up elements of a and b as needed
     M c = a*b;    // this relies on M having appropriate constructor(s) to copy or move the result of a*b into c

     M d;
     d = a * b;    //  this relies on M having appropriate operator=() to assign d to the result of a*b
}

The above implements operator*()as a member function. So, functionally, c = a*bis equivalent to c = a.operator*(b). The constqualifiers represent the fact that a matrix multiplication a*bdoes not generally change aor b.

以上实现operator*()为成员函数。所以,在功能上,c = a*b相当于c = a.operator*(b)。在const预选赛代表了一个事实,即矩阵乘法a*b通常不会改变ab

Example 2: operator*()as a non-member function

示例 2: operator*()作为非成员函数

Now, operator*()can also be implemented as a non-member (optionally a friend), with a skeleton that looks like

现在,operator*()也可以实现为非成员(可选 a friend),其骨架看起来像

class M   // our basic matrix class, different operator *
{
    public:

          // assume other constructors and members to set things up
       friend M operator*(const M &lhs, const M &rhs);
};

M operator*(const M &lhs, const M &rhs)
{
       //   implement checks on dimensions, throw an exception if invalid

       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

//   same main() as before

Note that, in this case, a*bis now equivalent to operator*(a, b).

请注意,在这种情况下,a*b现在等效于operator*(a, b)

If you want to use both forms, care is needed to avoid ambiguity. If both forms of operator*()are provided they are both valid matches in a statement like c = a*band the compiler has no means to choose one form over the other. The result is code not compiling.

如果您想同时使用这两种形式,则需要注意避免歧义。如果提供了两种形式的operator*(),它们都是类似语句中的有效匹配项,c = a*b并且编译器无法选择一种形式而不是另一种形式。结果是代码没有编译。

Example 3: overloading operator*()

示例 3:重载 operator*()

It is also possible to overload operator*()- for example, to multiply a matrix by a scalar.

也可以重载operator*()- 例如,将矩阵乘以标量。

class M   // a basic matrix class
{
    public:

          // assume other constructors and members to set things up
       M operator*(const M &rhs) const;    // as in first example

       M operator*(double scalar) const;    // member form
       friend M operator*(double scalar, const M &rhs);   // non-member form
};

M M::operator*(double scalar) const
{
       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

M operator*(double scalar, const M &m)
{
       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

int main()
{
     M a;
     M b;
        // set up elements of a and b as needed
     M c = b * 2.0;    // uses the member form of operator*() above

     M d;
     d = 2.0*a;        //  uses the non-member form of operator*() above
}

In the above b*2.0amounts to a call of b.operator*(2.0)and 2.0*ato a call of the non-member operator*(2.0, a). The member forms can only generally be used in expressions where the left hand operand is of type M. So 2.0*awill not work if only member forms of operator*()is provided.

以上b*2.0相当于非会员的通话b.operator*(2.0)2.0*a通话operator*(2.0, a)。成员形式通常只能用于左侧操作数为 类型的表达式中M。因此,2.0*a如果仅提供成员形式,则将不起作用operator*()

Discussion

讨论

Apart from concerns of ambiguity above, there are other things to be aware of when overloading operators.

除了上面的歧义之外,在重载运算符时还有其他事情需要注意。

  • It is not possible to change precedence or associativity of operators from their specification in language rules. So, in the expression a+b*c, the *will always have higher precedence than the +. This is also the reason it is not a good idea to overload ^for exponentiation in C++, since ^has a lower precedence than +in C++ (being a bitwise operation on integral types). So a + b^cis actually equivalent in C++ to (a + b)^c, not to a + (b^c)(which anyone with basic knowledge of algebra would expect).
  • The language specifies a set of operators, and it is not possible to create new ones. For example, there is no **in C++, such that a ** braises ato the power of b(which other languages can do), and it is not possible to create one.
  • Not all operators can be overloaded.
  • 不可能从语言规则中的规范中更改运算符的优先级或结合性。因此,在表达式中a+b*c*总是比 具有更高的优先级+。这也是^在 C++ 中重载求幂不是一个好主意的原因,因为^它的优先级低于+C++(整数类型的按位运算)。Soa + b^c实际上在 C++ 中等效于(a + b)^c,而不是a + (b^c)(具有代数基本知识的任何人都会期望)。
  • 该语言指定了一组运算符,并且无法创建新的运算符。例如,不存在**在C ++中,使得a ** b加注a到的功率b(其其它语言可以做),而这是不可能创建一个。
  • 并非所有运算符都可以重载。

One of the operators that cannot be overloaded in C++ is .*. So it is not possible to use such an operator like you would in Matlab. I would generally suggest NOT trying to get the same effect using other operators, because the above constraints will affect that (and cause expressions to give counter-intuitive behaviour). Instead simply provide another named function to do the job. For example, as a member function

在 C++ 中不能重载的运算符之一是.*. 所以不可能像在 Matlab 中那样使用这样的运算符。我通常建议不要尝试使用其他运算符获得相同的效果,因为上述约束会影响它(并导致表达式给出违反直觉的行为)。相反,只需提供另一个命名函数来完成这项工作。例如,作为成员函数

   class M
   {
       public:
         // other stuff

          M ElementWiseProduct(const M &) const;
   };

回答by ashbygeek

Most of the answers have already covered which operators are and are not overloadable, but none have discussed WHY some are mutable and some aren't.

大多数答案已经涵盖了哪些运算符是可重载的,哪些不可重载,但没有人讨论为什么有些是可变的,有些则不是。

The following is a quote from Bjarne Stroustrup (the guy who wrote c++) that I found in this stackoverflow answer. Pay particular attention to the third paragraph.

以下是我在这个 stackoverflow answer 中找到的 Bjarne Stroustrup(编写 C++ 的人)的引述。特别注意第三段。

When I decided to allow overloading of operator ->, I naturally considered whether operator . could be similarly overloaded.

At the time, I considered the following arguments conclusive: If obj is a class object then obj.m has a meaning for every member m of that object's class. We try not to make the language mutable by redefining built-in operations (though that rule is violated for = out of dire need, and for unary &).

If we allowed overloading of . for a class X, we would be unable to access members of X by normal means; we would have to use a pointer and ->, but -> and & might also have been re-defined. I wanted an extensible language, not a mutable one.

These arguments are weighty, but not conclusive. In particular, in 1990 Jim Adcock proposed to allow overloading of operator . exactly the way operator -> is.

当我决定允许 operator -> 重载时,我自然会考虑是否 operator . 可能同样超载。

当时,我认为以下论点是决定性的:如果 obj 是一个类对象,那么 obj.m 对该对象类的每个成员 m 都具有意义。我们尽量不通过重新定义内置操作来使语言可变(尽管出于迫切需要的 = 和一元 & 违反了该规则)。

如果我们允许重载 . 对于类 X,我们将无法通过正常方式访问 X 的成员;我们将不得不使用指针和 ->,但 -> 和 & 也可能已被重新定义。我想要一种可扩展的语言,而不是可变的。

这些论点是有分量的,但不是决定性的。特别是在 1990 年,Jim Adcock 提议允许运算符 重载。正是 operator -> 的方式。

A page on his websiteadds a little more:

他网站上的一个页面添加了更多内容:

Can I define my own operators?

Sorry, no. The possibility has been considered several times, but each time I/we decided that the likely problems outweighed the likely benefits.

It's not a language-technical problem. Even when I first considerd it in 1983, I knew how it could be implemented. However, my experience has been that when we go beyond the most trivial examples people seem to have subtlely different opinions of "the obvious" meaning of uses of an operator. A classical example is a ** b ** c. Assume that ** has been made to mean exponentiation. Now should a ** b ** c mean (a ** b) ** c or a ** (b ** c)? I thought the answer was obvious and my friends agreed - and then we found that we didn't agree on which resolution was the obvious one. My conjecture is that such problems would lead to subtle bugs.

我可以定义自己的运算符吗?

抱歉,没有。这种可能性已经考虑过多次,但每次我/我们都认为可能的问题超过了可能的好处。

这不是语言技术问题。甚至当我在 1983 年第一次考虑它时,我就知道它可以如何实施。然而,我的经验是,当我们超越最琐碎的例子时,人们似乎对运算符用法的“明显”含义有着微妙的不同意见。一个经典的例子是 a ** b ** c。假设 ** 已表示取幂。现在应该 a ** b ** c 表示 (a ** b) ** c 还是 a ** (b ** c)?我认为答案是显而易见的,我的朋友们也同意了——然后我们发现我们在哪个决议是显而易见的问题上没有达成一致。我的猜测是这些问题会导致微妙的错误。

So, while most operators can be overloaded, it was never intended for people to create arbitrary operators in c++.

因此,虽然大多数运算符都可以重载,但从来没有打算让人们在 C++ 中创建任意运算符。

回答by suhdonghwi

As other answers say, overloading operator.*is not possible.

正如其他答案所说,超载operator.*是不可能的。

But I got a good solution for your question, check here.

但是我为您的问题找到了一个很好的解决方案,请在此处查看

You can provide any methods in operator-ishform like:

您可以提供操作符-任何方法十岁上下的形式,如:

M <matrix_mul> N

回答by Ernest Friedman-Hill

It's as simple (and as difficult!) as defining a function named (in this case) operator*():

它就像定义一个名为(在本例中)的函数一样简单(也同样困难!)operator*()

Matrix operator*(const Matrix &m1, const Matrix &m2) ...

where Matrixis a class you've defined to represent matrices.

Matrix您定义的用于表示矩阵的类在哪里。