About the C++ overloaded operators - c++

Running the program below on my computer
#include <iostream>
class Int {
public:
Int(int x) : val{x} {}
Int operator++() {
std::cout << "Int::operator++()\n";
return ++val;
}
friend Int operator+(Int a, Int b) {
std::cout << "operator+(Int, Int)\n";
return a.val + b.val;
}
friend Int operator*(Int a, Int b) {
std::cout << "operator*(Int, Int)\n";
return a.val * b.val;
}
private:
int val;
};
int main()
{
Int a = 1, b = 2;
b = ++a + b * b;
return 0;
}
I got this output:
operator*(Int, Int)
Int::operator++()
operator+(Int, Int)
As far as I known, the prefix ++ has higher precedence than binary *. But in the output shown above, the prefix ++ is called after the binary *! Is it because the compiler treats the operator+ as a function call (which leads to Unspecified Behavior)? Can I always consider an overloaded operator as a function (which makes the behavior of x = x++ well-defined when x is an Int)?
Thanks!

Is it because the compiler treats the operator+ as a function call (which leads to Unspecified Behavior)?
Yes. But note that it doesn't matter if ++a is evaluated after b * b, because in the end, those two are added correctly, which respects operator precedence rules.
Your expression without the assignment is equivalent to:
operator+(a.operator++(), operator*(b, b))
The order of evaluation of function arguments is unspecified, so technically, ++a can be evaluated before b * b, but also the other way around.
Can I always consider an overloaded operator as a function (which makes the behavior of x = x++ well-defined when x is an Int)?
Yes and no. If Int does the same thing as the "normal" operators would do, then no (until C++17), because that would be undefined behavior. But if Int doesn't change x in x++ for example, then yes.

As far as I known, the prefix ++ has higher precedence than binary *.
Higher precedence doesn't mean that the prefix increment will be called before the multiplication operator, but in which order the parameters are bound to the corresponding operation.

Related

Operator overloading with different datatypes in C++

Take this for example:
float operator+(int a, float b)
{
Return b + (float)a;
}
void main()
{
int a = 10;
float b = 2.5f;
float c;
c = a + b; //works as intended
c = b + a; //results in type mismatch error
}
So my question:
Is there a way to have the parameters being accepted bidirectional without defining a second function?
It is not possible to overload operators for fundamental types.
You should only overload operators for classes that you have defined.
A solution for overloading a binary operator for your own class with heterogeneous arguments symmetrically, is to define the operator for first type, and make the second type implicitly convertible to the first.
Use float 'a' instead of int 'a',In default when adding int number to float it automatically assign higher precision. (Int + float outcome become float)

Why is & needed for member function pointers, not for normal function pointers?

I understand my second statement that "why & is not needed for normal function pointers" because function name itself is address of the function.
What I do not understand is why '&' is strictly needed for member function pointers?
Examples:
Normal function pointers:
int add(int a, int b) {
return (a + b);
}
int (*fp)(int, int);
fp = add;
(*fp)(2, 3) // This would give me addition of a and b, i.e. 5
Member function pointers:
class ABC {
public:
int i;
ABC() { i = 0; }
int addOne(int j) {
return j + 1;
}
};
// Member function pointer
int (ABC::*mfp)(int);
// This is what I am talking about. '&' in below line.
mfp = &ABC::addOne;
ABC abc;
std::cout << (abc.*mfp)(2) << std::endl;
It seems to me that the address-of operator is necessary for member function pointers because the right-hand side (rhs) of the declaration is a constant rather than a variable.
We wouldn't say
int (ABC::*mfp)(int);
mfp = ABC::addOne();
because that would be to invoke a function.
Furthermore, the scope resolution operator :: has the highest precedence in the C++ table of operator precedence:
https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/cpp/cpp-built-in-operators-precedence-and-associativity.md
The :: operator is evaluated before any other operators are on the rhs. I suppose the complier wonders "Hmmmm...what's that? That should be a function, but..." and then sees the address-of operator and knows what the developer needs.

Overloading ++ operator

I need to overload operators for a class of complex numbers
with 2 private members :
double real;
double imag;
this is the constructor
complex::complex(double a, double b) {
real = a;
imag = b;
}
This is the given prototype
complex complex :: operator++ (int x);
but I don't see how this would be overwritten since
int u = 1;
int h = 3;
u++3;
doesn't work
so how would this compile
I've only ever seen
variable++
--variable
How would this be overloaded?
The int x in the post-increment operator signature operator++(int x) is called a dummy parameter. It doesn't actually mean anything, other than to distinguish this from operator++() which is the pre-increment operator.
In the body of the function you write the logic for post-increment but just don't use x. It gets value 0 when you use variable++.
Technically you could write variable.operator++(5); and then the post-increment operator would be called with x being 5 but there's no real reason to want to do that.

optimizing binary arithmetic operations using move semantics

I'm experimenting with the rvalue references with a simple Vector class, trying to eliminate unneeded temporaries in binary operations. After a little bit of struggle, I found that with the following two overloads for operator+():
// overload called if right = lvalue and left = lvalue/rvalue
friend Vector<T> operator+(Vector<T> a, const Vector<T>& b) {
a += b;
return a;
}
// overload called if right = rvalue and left = lvalue/rvalue
friend Vector<T> operator+(const Vector<T>& a, Vector<T>&& b) {
b += a;
return std::move(b);
}
I can ensure that in an expression like auto x = a+b+c+d+...;, as long as at least a or b is a temporary, the move constructor will be called without creating any new temporaries.
On the other hand even if one of the values after a and b (say d) is lvalue, that should technically be enough to avoid the copy. Is there any optimization that can compiler can do by scanning a given expression to find at least one temporary and start calling the operator+ based on that value?
Example 1:
#include "vector.h"
Vector<double> get() {
return {0, 1, 2, 6};
}
int main (){
auto a = get();
auto b = get();
auto c = a + get() + b;
std::cout << c << std::endl;
return 0;
}
Output:
calling move ctor
calling move ctor
[0, 3, 6, 18]
Example 2:
#include "vector.h"
Vector<double> get() {
return {0, 1, 2, 6};
}
int main (){
auto a = get();
auto b = get();
auto c = a + b + get();
std::cout << c << std::endl;
return 0;
}
Output:
calling copy ctor
calling move ctor
[0, 3, 6, 18]
I guess the answer is negative. In your code
auto c = a + b + get();
The compiler must first call operator+() on a and b. This execution order is predefined in laguage specification I think. The compiler should not execute b + get() first, because different execution order can result in different return value for (a + b + get()), along with different side effects. So the order of execution should remain unchaged.
And is there something compiler can do with a + b part in the first place? As both a and b are l-value, compiler must choose a function which takes two l-value argumenets. It's not that compiler developers cannot add new optimizations, but that compiler should only do as specified in language. Suppose that compiler uses r-value version of your function for a + b. And then, in your function, a or b will be modified (as they are r-value), which is something not intended: why do we need to modify operands to get the sum of them?
And if you are willing to make a and b modifiable in your function, just can cast them as r-value before calling your function, like std::move(a) + std::move(b) + get()
Hope this helps.

Commutative property a[i] == i[a]

For a built in type integer array say
int a[10];
int i = 2;
a[i] = 10;
alternatively
i[a] = 10;
because
a[i] is a postfix expression that is *(a+i) or *(i+a) because commutative property of addition.
I want to achieve that for a userdefined type say
class Dummy
{
//
};
Is it possible?
If yes then how?
If no then why?
EDIT :-
I know it is ugly but following code compiles :-
g++ -dumpversion
4.3.3
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <malloc.h>
using namespace std;
int main()
{
string ArrayS[10];
2[ArrayS] = "ADASD" ;
cout << 2[ArrayS] << endl;
return 0;
}
It is impossible because "operator[] shall be a non-static member function with exactly one parameter" (standard §13.5.5/1), so you cannot define it such that the first argument is of native scalar type.
(Furthermore, a nonstatic operator overload call is interpreted as a member call, so the first operand cannot be implicitly converted, unlike a free function overload. This is one reason why free function overloads are preferred when possible.)
For better or worse, index[ object ] is a way to ensure that no operator[] overload gets called.
However.
"The expression E1[E2] is identical (by definition) to *((E1)+(E2))" (§5.2.1) and operator+ can be overloaded so long as one side is not native type. This leaves two options vulnerabilities: the "array" must be a class, or the "index" must be a class or enum.
You would then have to define a proxy type to hold the result of "addition," which defines an operator* overload. GCC does not support this, however. I'll look deeper into other platforms and references.
Edit: Ah, §13.6/13 overrides 5.2.1 and declares that, for the sake of interpreting an expression involving class or enumeration type, there are functions T& operator[](std::ptrdiff_t, T*); and T& operator[](T*, std::ptrdiff_t);. So that's that.
With C++, nothing is impossible. It is however, a terrible terrible idea. Don't do this.
#include <memory>
#include <stdlib.h>
#include <stdio.h>
void *aligned_malloc( size_t bytes, size_t alignment ) {
void *p = malloc( bytes + alignment ), *pa = reinterpret_cast<void*>( reinterpret_cast<size_t>(p) + alignment &- alignment );
reinterpret_cast<void**>(pa)[-1] = p;
return pa;
}
void aligned_free( void *pa ) {
void *p = reinterpret_cast<void**>(pa)[-1];
free( p );
}
struct SupportReverseIndexer
{
class IndexerReversal
{
static const size_t alignment;
friend struct SupportReverseIndexer;
friend class std::auto_ptr<IndexerReversal>;
struct SupportReverseIndexer* const m_parent;
IndexerReversal(struct SupportReverseIndexer* parent) : m_parent(parent) {}
void* operator new(size_t bytes) { return aligned_malloc(bytes, alignment); }
void operator delete(void* p) { aligned_free(p); }
static struct SupportReverseIndexer* getParent(IndexerReversal* pThis)
{
size_t iThis = reinterpret_cast<size_t>(pThis);
iThis += alignment >> 1;
iThis &= ~(alignment - 1);
return reinterpret_cast<IndexerReversal*>(iThis)->m_parent;
}
public:
operator size_t() { struct SupportReverseIndexer* const parent = getParent(this); return parent->indexer(this-parent->ir.get()); }
};
SupportReverseIndexer() : ir(new IndexerReversal(this)) {}
operator IndexerReversal*() { return ir.get(); }
private:
std::auto_ptr<IndexerReversal> ir;
size_t indexer(size_t index) { printf("Custom operator[] called, index = %i\n", index); return index; }
};
const size_t SupportReverseIndexer::IndexerReversal::alignment = 0x10000 * sizeof(SupportReverseIndexer::IndexerReversal);
int main(void)
{
SupportReverseIndexer sri;
int a = sri[2];
a = 3[sri];
a = (-5)[sri];
return 0;
}
No, really, DON'T DO THIS!!!!!
It is completely impossible to do. Potatoswatter correctly points out you couldn't possibly define any operator such as operator[](int, T), so overloading for an integer on the left is impossible.
But consider that this works:
struct foo
{
operator const foo*() const
{
return this;
}
};
int main()
{
foo f;
5[f]; // UB
}
Is there a way to utilize this? No:
5.2.1 Subscripting
A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “pointer to T” and the other shall have enumeration or integral type. The result is an lvalue of type “T.” The type “T” shall be a completely-defined object type.56) The expression E1[E2] is identical (by definition) to *((E1)+(E2)).
With E1 being an integral type, E2 must be a pointer type. So we can't inject user-behavior there.
The only thing left is + and *. We can't change operator+ for an integral type and a pointer, because that's defined by the language. The result of E1 + E2 is going to be a pointer, and we can't define operator* for a pointer either.
Therefore, injecting user-defined behavior is impossible.
a[i] = 10;
i[a] = 10;
because a[i] is a postfix expression that is *(a+i) or *(i+a) because commutative property of addition.
Yes. But a[i] == i[a] == *(a+i) == *(i+a) is because the notation a[i] in C is a syntactic sugar for the latter pointer arithmetic. Keyword here "in C". C++ in large tries to part ways from the pointer arithmetic. Thus the a[i] as a syntactic sugar is only supported on the POD for backward compatibility with the C. But that does not work on any C++ objects since the operations [] has clear semantic specified and it doesn't allow the C-like syntax tricks.
In the end, do not do it. The trick is an obscure remnant of the older times and doesn't have a single good reason to be reincarnated.
Any Dummy that was declared and used as an array will work the same way, if Dummy can be implicitly cast to an integer.
You will need to define the Dummy::operator[](const Dummy& ref) to exhibit the desired property. For instance:
#include <iostream>
class dummy
{
public:
int operator[](const dummy& ref) { return 0; }
};
int main(int argc, char *argv[])
{
dummy d1, d2;
std::cout << (d1[d2] == d2[d1] ? "Yes" : "No");
}
Of course this is probably not exactly what you're looking for, but the tricky bit will be to replace the return 0; and possibly the return type with something that you have to define and that satisfies your intent.
Why exactly would you want this? Is it just a mental exercise?