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

jk9hmnmh  于 2023-05-24  发布在  其他
关注(0)|答案(9)|浏览(231)

MATLAB数组支持矩阵运算和元素运算。例如,M*NM.*N。这是一种非常直观的方式来区分这两种不同的操作。如果我想在C++中实现类似的操作,我该怎么做?
我也可以创建一个新的运算符.*吗?如果是的话,谁能给予我一些指导?

mzaanser

mzaanser1#

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

. .* :: ?:
xwbd5t1u

xwbd5t1u2#

在C++中,有一个预定义操作符列表,其中大多数是可重载的(.* 不是)。此外,any name可以用作操作符,如:

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

iibxawm4

iibxawm43#

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

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)

bvn4nwqk

bvn4nwqk4#

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

fiei3ece

fiei3ece5#

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)
  • 增量-减量-两者都有2种形式(前缀)和(后缀)
  • ++ (Increment)
  • -- (Decrement)
  • 下标
  • [] (Subscript)
  • 函数调用
  • () (Function Call)
  • 地址、引用、指针
  • operator&()
  • operator*()
  • operator->()
  • 逗号
  • operator,()
  • 成员参考
  • operator->()
  • operator->*()
  • 内存管理
  • new
  • delete
  • new[]
  • delete[]
  • 转换
  • operator "type" () const
  • 不可修改运算符-不能重载的运算符
  • ?: (Conditional - Ternary)
  • . (Member Selection)
  • .* (Member Selection With Pointer To Member)
  • :: (Scope Resolution)
  • sizeof() (Object Size Information)
  • typeid() (Object Type Information)

所以知道这个列表将有助于回答你的问题。你能在C中创建一个“新运算符”吗?不!如果你想在C中实现类似的操作;我该怎么做?
你有四个选择:要么重载一个已经存在的可以重载的运算符,写一个函数或方法来执行你想要执行的计算类型,创建一个模板类型来为你做这些工作,或者最后一个最不常见的,但你也可以写宏来为你做这些工作。
有一个仅限标题的数学API库,经常与OpenGL图形API和OpenGL的着色器语言GLSL一起使用,该库具有许多与矢量,矩阵,四元数等相关的功能,以及可以对它们进行的所有必要函数和操作。这里是GLM的链接,你可以看看他们的文档以及他们的库实现,因为它是一个只有头的库或API。这应该会给予你对他们如何构造Vector和Matrix对象以及可以对它们进行的操作有一些了解。

1dkrff03

1dkrff036#

顺便说一句:我试图回答这个问题的部分。我也不想重复其他有价值的答案中的所有信息。赏金寻求的东西与所问的问题不同,所以我不回答这个问题。
提供矩阵乘法实际上相当简单。由于我并不打算描述数据结构来表示矩阵并完全实现对它们的操作和有效性检查,因此我只提供框架来进行说明。

    • 实施例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;
   };
sxissh06

sxissh067#

大多数答案已经涵盖了哪些操作符是可重载的,哪些是不可重载的,但没有人讨论过为什么有些是可变的,有些不是。
下面是我在this stackoverflow answer中发现的Bjarne Stroustrup(编写c的家伙)的一段话。特别注意第三段。
当我决定允许重载operator ->时,我自然会考虑是否是operator。也可能同样超载。
当时,我认为以下论点是结论性的:如果obj是一个类对象,那么obj.m对该对象的类的每个成员m都有意义。我们尽量不通过重新定义内置操作来使语言可变(尽管出于迫切需要,=和一元&违反了这一规则)。
如果我们允许的话。对于类X,我们将无法通过正常方式访问X的成员;我们必须使用指针和->,但是->和&也可能被重新定义。我想要一种可扩展的语言,而不是一种可变的语言。
这些论点很有分量,但不是结论性的。1990年,Jim Adcock提出允许操作符重载。就像operator ->一样。
他的网站上的一个页面补充了一点:
我可以定义自己的操作符吗?
这种可能性已经考虑过好几次了,但每次我/我们都认为可能的问题超过了可能的好处。
这不是一个语言技术问题。甚至当我在1983年第一次考虑它的时候,我就知道它是如何实现的。然而,我的经验是,当我们超越最琐碎的例子时,人们似乎对操作符的使用的“明显”含义有微妙的不同意见。一个典型的例子是aBc。假设表示取幂。现在aBc应该是指(ab)c还是a(b**c)?我认为答案是显而易见的,我的朋友们也同意--然后我们发现,我们没有就哪个决议是显而易见的达成一致。我的推测是,这样的问题会导致微妙的错误。
因此,虽然大多数运算符都可以重载,但在c
中,人们从来没有打算创建任意的运算符。

0dxa2lsx

0dxa2lsx8#

它是如此简单(和困难!)定义一个名为operator*()的函数:

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

其中Matrix是一个你定义用来表示矩阵的类。

ig9co6j1

ig9co6j19#

正如其他答案所说,重载operator.*是不可能的。
但是我有一个很好的解决方案,检查here
你可以在operator-ish 格式中提供任何方法,比如:

M <matrix_mul> N

相关问题