Reputation: 1001
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?
Upvotes: 47
Views: 54984
Reputation: 2499
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.
Upvotes: 9
Reputation: 761
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.
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.
A page on his website adds 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.
So, while most operators can be overloaded, it was never intended for people to create arbitrary operators in c++.
Upvotes: 3
Reputation: 36597
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;
};
Upvotes: 5
Reputation: 1035
As other answers say, overloading operator.*
is not possible.
But I got a good solution for your question, check here.
You can provide any methods in operator-ish form like:
M <matrix_mul> N
Upvotes: 1
Reputation: 10880
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.
Upvotes: 28
Reputation: 7895
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?
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:
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)
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?
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.
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 GLM You 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.
Upvotes: 7
Reputation: 302653
You cannot overload .*
(see Lightness' answer for standard text), but, interestingly enough, you can overload ->*
(similar to how you can overload ->
but not .
). If that's sufficient for differentiation, then have at it:
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)
.
Upvotes: 13
Reputation: 385098
No, you can't overload op.*
:
[C++03 & C++11: 13.5/3]:
The following operators cannot be overloaded:. .* :: ?:
Upvotes: 32
Reputation: 81674
It's as simple (and as difficult!) as defining a function named (in this case) operator*()
:
Matrix operator*(const Matrix &m1, const Matrix &m2) ...
where Matrix
is a class you've defined to represent matrices.
Upvotes: 1