我可以用C ++创建一个新的运算符吗?

时间:2011-12-08 01:33:11

标签: c++

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

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

9 个答案:

答案 0 :(得分:27)

不,你不能重载op.*

  

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

. .* :: ?:

答案 1 :(得分:15)

在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。因此,它不是一个新的&#39;运算符,用C ++术语,但它的使用方式完全相同,即使在ADL方面也是如此。此外,如果type很大,您可能希望存储const T&

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

如果你想要正确联想,那就会变得有点棘手。您需要RHS持有者和LHS持有者(后者在此LHSlt)并使用周围的运算符,使得正确的运算符具有比左运算符更高的优先级,例如, a |myop> b |myop>ca |myop> (b |myop> c)。然后你需要你的类型和持有者类型的函数作为lhs。

答案 2 :(得分:12)

您无法重载.*(请参阅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)

答案 3 :(得分:6)

  

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 ++中实现类似的操作;我怎么能这样做?

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

有一个只有标题的Math API库,经常与OpenGL图形API和OpenGL的Shader语言GLSL一起使用,这个库有许多功能,可以使用矢量,矩阵,四元数等,以及所有必要的功能和操作可以对他们做。以下是GLM的链接您可以查看他们的文档以及他们的库实现,因为它只是一个头文件库或API。这可以让您了解他们如何构建Vector和Matrix对象以及可以对它们执行的操作。

答案 4 :(得分:5)

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

答案 5 :(得分:3)

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

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

示例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的表达式中。因此,如果仅提供2.0*a的成员表单,则operator*()将无效。

<强>讨论

除了上面含糊不清的问题之外,在重载运算符时还有其他事项需要注意。

  • 无法在语言规则中更改运算符的优先级或关联性。因此,在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;
   };

答案 6 :(得分:2)

大多数答案已经涵盖哪些运营商是否可以超载,但没有人讨论过为什么有些是可变的而有些则不是。

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

  

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

     

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

     

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

     

这些论点很重要,但不是决定性的。特别是,1990年Jim Adcock提议允许运营商超载。完全是运营商的方式 - &gt;是

A page on his website增加了一点:

  

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

     

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

     

这不是语言技术问题。即使我在1983年首次考虑它,我也知道它是如何实现的。然而,我的经验是,当我们超越最微不足道的例子时,人们似乎对运营商使用的“明显”含义有着微妙的不同看法。一个经典的例子是** b ** c。假设**已经被认为是指取幂。现在应该a ** b ** c是指(a ** b)** c还是**(b ** c)?我认为答案很明显,我的朋友们同意了 - 然后我们发现我们不同意哪个决议是明显的。我的猜想是,这些问题会导致细微的错误。

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

答案 7 :(得分:1)

与定义名为(在本例中)operator*()的函数一样简单(也很困难!):

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

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

答案 8 :(得分:1)

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

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

您可以在operator- ish 表单中提供任何方法,如:

M <matrix_mul> N