What Operators Do I Have to Overload to See All Operations When Passing an Object to a Function

What operators do I have to overload to see all operations when passing an object to a function?

In C++11, you should add rvalue reference:

A(A&&);
void operator=(A&& a );

Passing an Object by reference to Overloaded Operator - C++

Operators are just like ordinary functions, just with "fancy" names :)

(e.g. operator+() instead of sum())

So, the same parameter passing rules that you apply to functions, can be applied to overloaded operators as well.

In particular, when you have a parameter that is not cheap to copy (e.g. an int, a float, are examples of cheap to copy parameters; a std::vector, a std::string, are examples of not cheap to copy parameters), and you observe this parameter inside your method (i.e. it's an input read-only parameter), then you can pass it by const reference (const &).

In this way, basically it's just like the address of the original argument is passed to the function, so there is no deep-copy involved. Deep-copies can be very expensive, e.g. think of a vector with a big number of elements.

So, to recap, you pass by const reference when:

  1. the parameter just is not cheap to copy (e.g. for ints, float, etc. just
    don't bother: passing by value is just fine)
  2. the parameter is observed in the function/operator implementation
    (i.e. it's an input read-only parameter)

operator overloading passed and returned arguments

Strictly speaking, passing by reference or by constant reference is not required: the code would work with passing by value. However, for most operators that would be inefficient, because passing by value entails copying the entire object - something you avoid when you pass by reference.

Same goes for returning a value: more often than not, you should return by value. However, there is one notable exception to this rule: all compound assignment operators need to return by reference, because they return (*this).

Using pointers instead of references is out of the question, but for a different reason: C++ requires operators to be compatible with specific signatures by type; a reference is type-compatible with a value of the corresponding type, but a pointer is incompatible. If you define an overload of an operator that takes pointers, C++ would not use your operators when working with values.

How to chain and serialize functions by overloading the | operator

First I assume you have some basics that look like this

#include <iostream>
struct vec2 {
double x;
double y;
};
std::ostream& operator<<(std::ostream& stream, vec2 v2) {return stream<<v2.x<<','<<v2.y;}

//real methods
vec2 translate(vec2 in, double a) {return vec2{in.x+a, in.y+a};} //dummy placeholder implementations
vec2 rotate(vec2 in, double a) {return vec2{in.x+1, in.y-1};}
vec2 scale(vec2 in, double a) {return vec2{in.x*a, in.y*a};}

So what you want is a proxy class for operations, where a proxy object is constructed with the function and the "other parameters". (I made the function a template parameter, which prevents the use of function pointers, and helps the optimizer to inline, making this nearly zero overhead.)

#include <type_traits>
//operation proxy class
template<class rhst, //type of the only parameter
vec2(*f)(vec2,rhst)> //the function to call
class vec2_op1 {
std::decay_t<rhst> rhs; //store the parameter until the call
public:
vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
vec2 operator()(vec2 lhs) {return f(lhs, std::forward<rhst>(rhs));}
};

//proxy methods
vec2_op1<double,translate> translate(double a) {return {a};}
vec2_op1<double,rotate> rotate(double a) {return {a};}
vec2_op1<double,scale> scale(double a) {return {a};}

And then you simply make that chainable

//lhs is a vec2, rhs is a vec2_operation to use
template<class rhst, vec2(*f)(vec2,rhst)>
vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) {return lhs=op(lhs);}

Usage is simple:

int main() {
vec2 v2{3,5};
v2 | translate(2.5) | rotate(30) | translate(3) | scale(2);
std::cout << v2;
}

http://coliru.stacked-crooked.com/a/9b58992b36ff12d3

Note: No allocations, no pointers, no copies or moves. This should generate the same code as if you just did v2.translate(2.5); v2.rotate(30); v2.scale(10);
directly.

Is there a better way to overload operators on objects without making tons of copies?

This compiles and should work fine:

#include <vector>

template <typename T> struct matrix
{
std::vector<T> data;
std::size_t lines, columns;

matrix() = default;
matrix(const std::size_t L, const std::size_t C);
void print();
};

template <typename T>
matrix<T> operator+(const matrix<T>& A, const matrix<T>& B);

template <typename T>
matrix<T> operator+(const matrix<T>& A, const matrix<T>& B)
{
matrix<T> rtrn(A.lines, A.columns);
for (std::size_t i = 0; i < A.data.size(); i++)
rtrn.data[i] = A.data[i] + B.data[i];
return rtrn;
}

You could also keep the current pattern of using the left input as a copy for the output. It is reasonably efficient but may increase code size.

template <typename T>
matrix<T> operator+(matrix<T> A, const matrix<T>& B);

template <typename T>
matrix<T> operator+(matrix<T> A, const matrix<T>& B)
{
for (std::size_t i = 0; i < A.data.size(); i++)
A.data[i] += B.data[i];
return A;
}

Normally, I would advise against this pattern because it invokes a copy that could instead be folded into the computation (reducing the total number of memory operations). However, you use std::vector internally and that always zero-initializes its elements. Therefore compared to the version above, the copy only replaces one memset with a memcpy. Not much worse.

Further reduction in allocations

If you want to reduce the number of temporary vectors further in chained operations such as C^(ExpFunc(A + B)) you can change the operations to work on expression objects, similar to what Eigen does.

C++ overload operator for class object acting as stream

For your function to work at all, you'll need to use references to TcpSocket for the input argument as well as the return type. Also val needs to be T const&, not T&.

template<typename T>
TcpSocket& operator<<(TcpSocket& sock, T const&val)
{
unsigned char bytesOfVal[] = //parse bytes of val here...

sock.Write(bytesOfVal, sizeof(bytesOfVal));
return sock;
}

Now, you can use:

sock << 45.8 << 33.9 << "numbers";

If you also want to be able to use:

sock << 45.8 << 33.9 << "numbers" << std::endl;

you'll need another function.

TcpSocket& operator<<(TcpSocket& soc,
std::ostream&(*f)(std::ostream&))
{
std::ostringstream s;
s << f;
return sock << s.str();
}

Operators defined only one way - C++

You should implement either a conversion between integer and complex number or define both operators:

complex operator+(int a, const complex& b)

and

complex operator+(const complex& a, int b)


Related Topics



Leave a reply



Submit