Operator Overloading in a correct way. How?

Well... I observed, as a non-professional programmer that "overloading operators" has some strict rules and some conventions... so any operator can differ from another.
In order to have a clearest idea, I'd like to ask you to specify, for every operator, the correct (or best) way to overload it.

I try to clarify my question.

There are cases where you define &operator and cases where you define operator (without "&"). There are cases where operator are defined as "friend" inside class, and other cases where operator is declared externally.

example: ostream &operator<<
(why it uses & ??)

So can we have a summary for all kind of operators?
You can think of operators as functions with special names.
To answer your question, the operator returns a reference to an std::ostream.

You need to use references with streams because streams cannot be copied. A reference in C++ is like an alias for the original variable.

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main()
{
    int i = 5;
    int &ri = i; // reference to i

    ri = 777;
    std::clog << "Surprise, surprise: " << i << '\n';
}
Surprise, surprise: 777


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int & func()
{
    static int n = 0;

    std::clog << "n is " << n << '\n';
    return n;
}

int main()
{
    func() = 100;
    func();
}
n is 0
n is 100

Nobun wrote:
There are cases where you define &operator and cases where you define operator (without "&"). There are cases where operator are defined as "friend" inside class, and other cases where operator is declared externally.

example: ostream &operator<<
(why it uses & ??)
The & is not related to the "operator" part, it is related to the return value - in your example, you have the function "operator<<" which returns a std::ostream by reference (the & of course means 'reference'). The return type is "std::ostream &", and the function name is "operator<<". The reason the & is touching the name of the function instead of the name of the type is due to convention derived from the behavior of declaring multiple variables:int* a, b; //a is a pointer, b is not

Operator overloading is discussed on the tutorial on this site:
http://www.cplusplus.com/doc/tutorial/classes2/
About half way down is a table explaining how to define the various operator functions.

The reason some are friend functions is because they need to have access to a classes private data but cannot be member operator functions - member operator functions only work if the class is the left-hand type; if it is the right-hand type, you need to declare the function globally, or as a static member of the class, with the second parameter being the class.

Operator overloading is pretty much free-form, but conventions are in place for the sake of good design.
Thank to all for replies...
... I readed the link. L B.... but I looked at it also before posting my question, but it doesn't explain me clearly my question (or better... I am unable to find the answer I am searching)

In the examples showed, reference are never used.

For example.... for ostream I never understood why it must have a return value and must used references (thank for explain me that &operator refers to return value, it wasn't clear for me).

Mororver... the writing in that link is not complete and clear... for example I don't find nothing that mention that logic operators should define OUTSIDE class with 2 parameters (I found someone uses also the keyword inline...... inline bool operator < (const T & t1, const T & t2) ).

And for example... why operator+ is NOT &operator+ ???
> for ostream I never understood why it must have a return value and must used references
To allow chaining std::cout << "hello " << foo() << " world";
`<<' also modifies the stream object, and you pretend to refer to the same object in the subsequent calls (besides it can't be copied). So it must return a reference to the stream object.

> I don't find nothing that mention that logic operators should define OUTSIDE class with 2 parameters
Convention.
Some operators can be defined in therm of others, the idea is to limit what you put inside your class (preferring non-member non-friend functions to member functions)
By instance, a+b could be defined as
1
2
c=a;
c+=b;
So you need only to define the compound assignment, and template magic may do the rest (check out boost_operators )

The same could be say about `<' that allows you to define relational operators (check out rel_ops namespace)


> why operator+ is NOT &operator+ ???
Do as the int
42+13 = 54; does not compile, so a+b = c; shouldn't compile either
"inline" just allows the function to be defined multiple times (e.g. it could be in a header that is included in several source files). It allows the compiler to perform optimizations sooner than later.
^ you put the chicken before the egg.
inline functions must go in headers, so they are threat especially to avoid the `multiple definitions' issue.
About inline keyword... I have no idea about how to use it seeing I never use that keyword...
... in order to avoid multiple definitions in several source files, however, I'm used to simply start my headers with

1
2
3
4
5
6
7
#ifndef MY_UNIQUE_NAME
#define MY_UNIQUE_NAME

//my header context

#endif
//end of file 


----------------

@ne555... I need some time to reflect about 2nd part of your reply. The "stream" part is clear. Thank a lot for answer... in the next days I could re-post again if something is not clear (for the moment I need time to think and read)
Nobun wrote:
in order to avoid multiple definitions in several source files, however, I'm used to simply start my headers with
That prevents multiple definitions within each individual source file - the header is still included in multiple source files and the definitions exist in each source file, which is multiple times overall.
I always used this technique for my programs (with multiple source files) and I never had any problem neither in compiling phase, nor in linking phase, nor in runtime.

Simply I NEVER put what a function (or class) do inside an header (and also NEVER put there initialize scopes)

example:
1
2
3
4
5
6
//header.h
#ifdef UNIQUE
#define UNIQUE

bool is_pair(int val);
#endif 


1
2
3
4
5
6
7
8
//header.cpp
#include "header.h"

bool is_pair(int val) {
  int k = val % 2;
  if(k==0) return true;
  else return false;
}


So I don't understand where is an issue O.o
(Any source must know the prototype of functions / classes it uses, else it would return an error on compile time).
Last edited on
That's because there are cases where multiple definitions within the same file are not allowed, but multiple definitions across multiple files are allowed as long as they are the same. It's know as the One Definition Rule:
http://en.wikipedia.org/wiki/One_Definition_Rule

Example:

Within the same source file, you cannot define a template multiple times:
1
2
3
4
5
6
7
8
9
template<typename T>
void f() //first definition
{
}

template<typename T>
void f() //illegal, second definition
{
}
However, across multiple files you can repeat the definition, as long as it is the same:
1
2
3
4
5
6
7
8
9
10
11
//source1.cpp
template<typename T>
void f() //first definition in this source file
{
}

//source2.cpp
template<typename T>
void f() //first definition in this source file, fine
{
}
The same is not true of normal functions, which can only be defined once in your entire program.


Those include guards you put on your header only work within the same source file, they are reset when the compiler moves on to a different source file.
Last edited on
Err... Sorry if I appear stupid, but I don't understand again... moreover seeing your example where you use Braces (so I assume you are thinking about writing the actual code for the template function of your example).

As I said I NEVER put in a header the actual code of a function or class, but the actual code it is "defined"one time only (in one unique .cpp source file of the source tree).
So, in my examples, the headers contains only the prototypes without explicitly saying how actually they works.... It delegates to linker to solve all entry points.... (surely I am missing something)
When it comes to templates, you must provide the definition for templated functions and classes.

Try and compile this code, I guarantee it will not work:

Template.h
1
2
template<typename T>
void ShowValue(T t);
Template.cpp
1
2
3
4
5
6
7
8
#include <iostream>
#include "Template.h"

template<typename T>
void ShowValue(T t)
{
    std::cout << "The value given is \"" << t << '"' << std::endl;
}
Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include "Template.h"

int main()
{
    int x = 7;
    double y = 3.1415926;
    std::string z = "This is text";

    ShowValue(x);
    ShowValue(y);
    ShowValue(z);

    ShowValue<int>(4);
    ShowValue<double>(1.2345);
    ShowValue<std::string>("More text");
}
Ah you are right. it was surprising for me... (I trusted you but I needed to see what kind of error would occur) I never used template so I didn't know that thing.

However, taking a brief look on Standard Template Library header sources (only quicky becouse they are too advanced for my limited skill), I saw that templates are not "inline".
So.... again.... I am not able to understand:
1) what is the scope of "inline" keyword and when it is used
2) (new) how to implement correctly a template class/function in a multi-module program and avoiding multiple-definitions (STL libs use the same teqnique like #ifndef UNIQUE, #define UNIQUE, code, #endif) (but surely I am missing something).

Sorry for insisting so long in this (out-of-topic) point but it is a thing that continue to be very criptic for me
Last edited on
Nobun wrote:
I saw that templates are not "inline".
You don't have to use the 'inline' keyword for a function to be inline. These two classes are identical:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct C1
{
    void f()
    { //definition here makes it implicitly inline
        std::cout << "C1::f" << std::endl;
    }
};

struct C2
{
    inline void f() //inline keyword is optional
    {
        std::cout << "C2::f" << std::endl;
    }
};


1. I'm not sure what you mean by the 'scope' of "inline", but it is used usually when you want to let the compiler know it has permission to inline a function or to allow for ODR to activate. When the compiler 'inlines' a function, that means that it takes the behavior of the function and substitutes it directly into where the function is called, like a macro - the benefit is that it eliminates the process of calling the function, passing arguments on the stack, etc. - it is just an optimization to make code run faster. it is not always possible, though.

An example of when inline is required is when you define a non-template non-class function in a header and include that header in multiple source files - without inline, the code will not link because the linker will see multiple definitions of the function. With inline keyword, the compiler will memorize the function and ignore it when it sees it again, so the linker only sees the definition once.

2. For template classes and functions, just define them completely in the header. In my example above, you would remove Template.cpp and change the Template.h header to:
1
2
3
4
5
6
7
8
9
10
11
#ifndef MyTemplateHeaderIncludeGuard
#define MyTemplateHeaderIncludeGuard
#include <iostream>

template<typename T>
void ShowValue(T t)
{ //complete definition in header
    std::cout << "The value given is \"" << t << '"' << std::endl;
}

#endif 
In this case you actually don't need include guards, but they are good practice anyway.

The same applies to classes; you would provide the definitions to all the functions inside the class in the header. In fact, this is allowed for classes even if they are not templates, because of how the functions are implicitly inline by being defined within the class.
> An example of when inline is required is when you define a non-template non-class function in a header
Again, http://cplusplus.com/forum/general/97556/#msg523783
I think I undestood, finally.... Thank for being so patient :)

Coming back to the original question... after re-reading also the "classes II" file comparing your writings a lot of things are more clearer now... thank (expecially for clarifying the << and >> operators) :)

But the "refererence" thing is not completely clear, again. The example in "classes II" is quite confusing for me (I don't understand how REFERENCE works in operator= example....

1) the example showed a case where a pointer - called "b" - is assigned assuming the address of a value - called "a"... that example it seems not involve references... and I cannot figure how references can work in operator=). So... when reference are usually used?

------------------

Aside from that.... I have a set of other detailed subquestions of things not clear after re-reading "classes II" with your precious help

2) operators of the first row (+, - etc), if members, have not any parameter. While, if functions, have only a parameter "A". But if I'd like, for example, overload operator+ to sum an object of class A with another thing like
A a = 1; a = a + 5;
or like
A a = 1; B b = 2; a = a + b;
is it possible to do it throughout GLOBAL operator+(A) creating a function A operator+(B)? (If I understood well the reply should be "NO" if it is a member function).

3) common practices.... I know that most suggested practice for <, >, == and != is to define a GLOBAL function more than a member functions (there are articles that explains why, but I don't read too deeply... I keep the information and trust). There are other "common" practices for other operators that allows both chance (I mean, cases where one solution is generally preferred over the other like in the example I wrote)?

4) operator() can have an arbitrary number of params, right? If so... what are the suggestions usually followed to avoid possible conflicts for the usage of () to call a constructor and the usage of () to activate your "special action in case of overload"?

.... other questions will be posted, but I stop here for the moment.... they are still a lot :)
Last edited on
Nobun wrote:
But the "refererence" thing is not completely clear, again. The example in "classes II" is quite confusing for me (I don't understand how REFERENCE works in operator= example....

1) the example showed a case where a pointer - called "b" - is assigned assuming the address of a value - called "a"... that example it seems not involve references... and I cannot figure how references can work in operator=). So... when reference are usually used?
Can you copy and paste the code/example you saw? I can't find what you're talking about.

operator= takes the object being assigned to it by reference for efficiency purposes, and by const reference if it also does not need to change the object being assigned to it. It returns the object being assigned to for the sake of chaining:
1
2
3
4
int a, b, c;
a = b = c = 7;
//same as:
a = (b = (c = 7));
> operator() can have an arbitrary number of params, right?
> If so... what are the suggestions usually followed to avoid possible conflicts
> for the usage of () to call a constructor
> and the usage of () to activate your "special action in case of overload"?
There is no possible ambiguity,
You can't call a constructor in an already constructed object.

> operators of the first row (+, - etc), if members, have not any parameter
You can't change the arity of operators. However, they can have multiple meaning.
By instance, `a-b' (subtract) or `-3' (sign)
`a*b' (multiply) or `*it' (dereference)

> is it possible to do it throughout GLOBAL operator+(A) creating a function A operator+(B)?
operator overloading work the same as functions.
They are for clarity, so `error.assign( subtract(multiply(A,x), b) )' becomes `error = A*x - b'

┬┐Can you create a global function to add an object from class `A' and class `B' ?
Another link to information. The overloading conventions are specific for each operator. I'm not sure that you are going to get a complete list of conventions in one post but as you find a need for each concept you'll probably have to research that at that time.
http://www.parashift.com/c++-faq-lite/operator-overloading.html

Generally, you want operators on user types to do the same thing as they do for built in types. + adds, - subtracts, ++ increments, -- decrements, * multiplies, = assigns, and so forth. The language can't stop you from doing something dumb like coding the operator+ as operator- or anything else.

Additionally, since you can do this.

1
2
int i(1), j(3), k(6), l(0);
l = i + j + k;


You also want to be able to do that for user defined types with operator+ defined. This is why operator+, operator=, and so forth must return the result.
Topic archived. No new replies allowed.