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 di
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
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*b
is equivalent to c = a.operator*(b)
. The const
qualifiers represent the fact that a matrix multiplication a*b
does not generally change a
or b
.
Example 2: operator*()
as a non-member function
Now, operator*()
can also be implemented as a non-member (optionally a friend
), with a skeleton that looks like
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*b
is now equivalent to 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*b
and the compiler has no means to choose one form over the other. The result is code not compiling.
Example 3: overloading operator*()
It is also possible to overload operator*()
- for example, to multiply a matrix by a scalar.
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.0
amounts to a call of b.operator*(2.0)
and 2.0*a
to 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*a
will not work if only member forms of operator*()
is provided.
Discussion
Apart from concerns of ambiguity above, there are other things to be aware of when overloading operators.
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^c
is actually equivalent in C++ to (a + b)^c
, not to a + (b^c)
(which anyone with basic knowledge of algebra would expect).**
in C++, such that a ** b
raises a
to the power of b
(which other languages can do), and it is not possible to create one.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
class M
{
public:
// other stuff
M ElementWiseProduct(const M &) const;
};
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.
In C++, there's a list of predefined operators, most of which are overloadable (.* is not). Additionally, any name can be used as an operator like:
#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&
.
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.
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 LHSlt
here) and use surrounding operators such that the right one has higher precedence than the left one, e.g. a |myop> b |myop>c
is a |myop> (b |myop> c)
. Then you need the function for both your type and your holder type as the lhs.