我可以在c++中创建一个新的操作符吗?

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

本文关键字:一个 操作符 c++ 创建 我可以      更新时间:2023-10-16

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

我也可以创建一个新的操作符.*吗?如果有,有人能给我一些指导吗?

没有,你不能过载op.*:

[C++03 & C++11: 13.5/3]:下列操作符不能重载:

. .* :: ?:

在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);
}

免责声明:严格来说,这被翻译成(5 < myop) > 2,也就是LHSlt<int, decltype(myop)>(5) > 2。因此,在c++术语中,它不是一个新的"操作符",但它的使用方式完全相同,甚至在ADL中也是如此。另外,如果type很大,您可能希望存储const T&

请注意,您可以对任何可以在类外部定义的二进制操作符执行此操作;优先级是基于两边(<>)的优先级。因此,您可以按此优先顺序拥有*myop*, +myop+, <<myop>>, <myop>, |myop|

如果你想要右结合性,就有点棘手了。您将需要一个RHS-holder和一个LHS-holder(后者在这里是LHSlt),并使用周围的操作符,使右操作符的优先级高于左操作符,例如a |myop> b |myop>ca |myop> (b |myop> c)。然后,您需要将类型和持有人类型的函数都作为lhs。

不能重载.*(参见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';
}

这将打印Int(30), Int(11)

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

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

也可以创建一个新的操作符。*吗?如果有,有人能给我一些指导吗?

对于第一部分,您可以重载大多数操作符,也有一些操作符不能重载,c++中的操作符列表如下:

  • 算法

    • + (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)
  • 下标
    • [] (Subscript)
  • 函数调用
    • () (Function Call)
  • 地址、引用、指针

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

    • operator,()
  • 成员引用

    • operator->()
    • operator->*()
  • 内存管理
    • new
    • delete
    • new[]
    • delete[]
  • 转换
    • operator "type" () const
  • NON Modifiable operator -不能重载的operator

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

所以知道这个列表将有助于回答你的问题。你能创建一个"新操作符"吗?在c++中?不!如果你想在c++中实现类似的操作;我怎么能做到呢?

你有4个选择:要么重载一个已经存在的可以重载的操作符,编写一个函数或方法来完成你想要执行的计算类型,创建一个模板类型来完成你的工作,或者最后一个是最不常用的,但你也可以编写宏来完成它们。

有一个头只有数学API库,它经常与OpenGL图形API和OpenGL的着色器语言GLSL一起使用,这个库有许多与向量,矩阵,四元数等一起工作的功能,以及所有必要的函数和操作。这里是GLM的链接,你可以看看他们的文档以及他们的库实现,因为它是一个头文件库或API。这应该会让你对他们如何构造Vector和Matrix对象以及可以对它们进行的操作有一些了解。

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

提供一个矩阵乘法实际上是相当简单的。由于我不打算描述数据结构来表示矩阵,也不打算在它们上面完全实现操作和有效性检查,所以我只提供一些框架来说明。

例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
}

上面的代码将operator*()作为成员函数实现。因此,从功能上讲,c = a*b等同于c = a.operator*(b)const限定符表示这样一个事实,即矩阵乘法a*b通常不会改变ab

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

现在,operator*()也可以实现为非成员(可选的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

注意,在这种情况下,a*b现在相当于operator*(a, b)

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

示例3:重载operator*()

也可以重载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
}

上面的b*2.0相当于对b.operator*(2.0)的一次调用,2.0*a相当于对非成员operator*(2.0, a)的一次调用。成员形式一般只能用于左操作数为M类型的表达式中。因此,如果只提供operator*()的成员表单,2.0*a将无法工作。

除了上面的歧义问题外,在重载操作符时还需要注意其他事项。

  • 在语言规则中不可能改变操作符的优先级或结合性。因此,在表达式a+b*c中,*的优先级总是高于+。这也是为什么在c++中重载^求幂不是一个好主意,因为^的优先级比c++中的+低(对整型进行按位操作)。因此,a + b^c在c++中实际上相当于(a + b)^c,而不是a + (b^c)(任何具有基本代数知识的人都会期望)。
  • 该语言指定了一组操作符,不能创建新的操作符。例如,c++中没有**,因此a ** ba提升到b的能力(其他语言可以做到),并且不可能创建一个。
  • 不是所有的操作符都可以重载。

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

   class M
   {
       public:
         // other stuff
          M ElementWiseProduct(const M &) const;
   };

大多数答案已经涵盖了哪些操作符可重载和不可重载,但没有一个回答讨论为什么有些操作符是可变的,而有些操作符不是。

以下是Bjarne Stroustrup(编写c++的家伙)的引用,我在这个stackoverflow答案中找到了。要特别注意第三段。

当我决定允许重载操作符->时,我自然会考虑是否为操作符。可以类似地重载。

当时,我认为下列实参是结论性的:如果obj是一个类对象,那么obj。M对该对象所属类的每个成员M都有意义。通过重新定义内置操作,我们尽量不让语言变得可变(尽管对于=和一元&,出于迫切需要,违反了该规则)。

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

这些论点很重要,但不是决定性的。特别是在1990年,吉姆·阿德考克提议允许运营商超载。就像算子->是。

他的网站上的一个页面增加了一点:

可以定义自己的操作符吗?

对不起,没有。这种可能性已经考虑过好几次了,但每次我/我们都认为可能的问题大于可能的好处。

这不是语言技术问题。甚至当我在1983年第一次考虑它的时候,我就知道它可以如何实现。然而,我的经验是,当我们超越最琐碎的例子时,人们似乎对"显而易见的"事物有着微妙的不同看法。操作符用法的含义。一个经典的例子是A ** b ** c。假设**表示幂。现在a ** b ** c是指(a ** b) ** c还是a ** (b ** c)?我认为答案是显而易见的,我的朋友们也同意——然后我们发现,我们在哪个解决方案是显而易见的问题上意见不一。我的猜想是这样的问题会导致微妙的错误。

所以,虽然大多数操作符都可以重载,但在c++中从来没有打算让人们创建任意的操作符。

与定义一个名为(在本例中)operator*():

的函数一样简单(也一样困难!)
Matrix operator*(const Matrix &m1, const Matrix &m2) ...

其中Matrix是您定义的用于表示矩阵的类。

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

但我有一个很好的解决方案,看看这里。

您可以在operator-ish形式中提供任何方法,例如:

M <matrix_mul> N