operator overloading and static functions

Feb 18, 2011 at 4:33am
Hi All,

It is important that overloading of = is defined for non static functions only.
This has something to do with lvalues.

Q1. can someone please explaining what this exactly means.
Q2. if i have a class of which i have many many overloaded operators. How do i make sure that if i declare a static object of that class, I do not end up using operators which have such constraints ??

Thx a lot.

~Cheers!
Navderm
Feb 18, 2011 at 1:48pm
I don't understand your initial statement. One can overload operator= for types not for (non static)functions. Can you clarify?
Feb 18, 2011 at 4:29pm
@jsmith:
I mean if i have a class which has = overloading. then i cannot use that = overloading with a static class object.
Also, i cannot use = overloading unless i have a user defined type either as member parent or as output.
Feb 18, 2011 at 6:58pm
I think my reply to one of your previous posts confused you. Sometimes I use way too much terminology.

- non-static member variables are associated with objects of the class
- the contents (i.e. values) of non-static member variables can differ from object to object
- only non-static member functions can read and write to non-static member variables
- you can call non-static member function only using objects; like "objectName.methodName(arguments)"
- modifying the value of non-static member variable modifies it only for the current object

- static member variables are associated with the class, but not with particular objects of the class
- the contents (i.e. values) of static member variables are shared between all objects
- all member functions can read and write to static member variables at all times
- particularly static member functions can read and write to static member variables of the class, but not to non-static member variables
- all functions (even non-member ones) can read and write static member variables if they have proper access (if the member variables are public, or the accessing functions are friends, or the accessing functions are member functions of a friend class)
- static member functions can be used without objects of the class; they can be called with the scope resolution operator like "ClassName::methodName(arguments)"

That being said, you can use non-static member functions on any object, no matter if it is static variable or automatic variable or non-static member variable. You need an object though. You don't need object for the static member functions.

There are two ways to implement operators: using non-member functions and using member functions.

When an operator is implemented as non-member function, then the arguments of the operator are directly provided by the operands in the operation where the operator was used. Because non-member functions have access restrictions for member variables, usually the operator non-member function is declared friend in the class to increase the access permissions.

When an operator is implemented as member function, the object to which the function is applied is the first operand of the operator. The remaining operands are turned into arguments for the member function.

Why assignment has to be a member function. For several reasons. But they are complex.

Calling non-member function is done with the functional notation f(exp1, exp2,..., expN), which requires that exp1, exp2.. expN are evaluated first, then the result is converted to the types required by f and then f is executed. Calling member function is done with the dot notation exp1.f(exp2,..., expN), which again requires that exp1, exp2.. expN are evaluated, but then the result from exp2 to expN is converted to the types required by Class::f(...) where Class is the type of exp1. The type of exp1 is not converted. It is used as is. Consequently, when you implement some operator as a member function, no conversions are applied to the first operand. It doesn't matter that you are using operators with the infix notation. The semantics for operators are derived from the semantics of the above two notations - functional and dot - depending on whether the operator was implemented as friend function or member function.

The assignment operator is in a special set of operations that the language likes to control. If the assignment operator was implemented as friend function, it would have to accept reference as first argument, because it has to modify its first operand. Something like:
T operator= (T&, T);
However, then conversions to T& would be permitted. References to objects of types (classes) that derive from T would be converted to references to the T-part of those objects. Then the assignment would be performed only on the T-part of the object whose actual type is some type derived from T. This is called slicing and it is usually semantically inconsistent, unintended, etc. That is, this would be a bug. This is why, the language requires that assignment operators are implemented as member functions. Then the first operand is not implicitly converted or anything, and slicing does not occur.

There is more to tell. Particularly you should investigate how the language treats the special set of operations: default constructor (default initialization), copy constructor (copy initialization), destructor (clean-up), and copy assignment. Otherwise you won't be completely in the clear on the subject. I hope someone else can recommend a book on those topics.

Regards
Feb 18, 2011 at 9:23pm
Since we are on the subject, I want to a make remark about some peculiarity. It is probably standard compliant and it certainly compiles with mingw gcc 3.4.5, but it is somewhat undesirable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct S {
    int a;
    int b;

    S operator= (S s)
    {
        a = s.a;
        b = s.b;

        return s;
    }
};

int main()
{
    int(5) = int(6); //error; int(5) is not lvalue
    (S){1, 2} = (S){2, 3}; //no error
}

Here I create temporary of some fundamental type (int in this case) and try to assign to it another temporary. This does not work, because temporaries are not variables and consequently can not be assigned. The compiler generates "lvalue required as left operand of assignment" error.

On the second line however, I create temporary of my user type S. Then I assign to this temporary the value of another temporary of the same type and this works ok, despite that the left hand side is still not lvalue.

Now, I know why this happens. It happens because non-const member functions (methods) can be called on temporaries. Had the assignment been a non-member function, then the second statement in main would have produced the same error as the first. So to answer part of the question of the original poster - not only declaring the assignment as member function does not guarantee that the left hand side is lvalue, but exactly the opposite, it allows the left hand side to be non-lvalue, i.e. temporary object in this case. This is inconsistent with the logic of that same operator for fundamental types, but there you go.

The situation is the same if the assignment operator was implicitly generated. I mean, with the compiler generated version of the assignment operator for S, assignment to temporaries is allowed again.

Regards
Feb 18, 2011 at 9:50pm
Hi,
Thankx simeonz, that helps a lot.!

By the way I am learning C++ with the book by Stroustrup. Could you suggest a decent level project which i could work on so that i have to end up using every such OO concepts ?

~Cheers!
Neeraj
Feb 19, 2011 at 12:28am
Stroustrup ... you are braver soul than me. Anyways, regarding the project. I don't claim to have matured per se. I am going to give you my personal view.

The most arcane and difficult features I usually encounter while trying to develop classes intended to support my development. Like:
- smart pointers for playing with operator overloading
- geometry, arithmetic, numerical computing classes for design of type hierarchies
- design patterns for exercising polymorphism

Implement smart pointers for reference counting, deep copying, proxy to out-of-memory data, etc. You can play with that for the rest of your life. There are usually library solutions for most of this, but since it is project for learning, there is nothing to stop you.

Classes for shapes. This is the most overused demonstration of design problem in OO. Anyways, creativity is rewarded here. And usually you end up with unique solution that matches your personal style. Arithmetic classes for big integers, rational numbers, fixed point arithmetic, etc. are also cool. Numerical computing of elementary functions and their compositions can be interesting, although I have never tried that.

Many design patterns are about polymorphism. You can implement the popular patterns to exercise on that. You will discover interesting idioms.

Anything will do, but I doubt that even STL uses all OO concepts. The boost library may be does, but this is not saying much.

Regards
Last edited on Feb 19, 2011 at 12:29am
Feb 20, 2011 at 12:31am
I just looked online for some shape class features.
I initially could not find any good ideas for it... now I have plently. I'll sit to complete this project. Should teach me loads of stuff !!!

Thx

~Cheers!
Neeraj
Topic archived. No new replies allowed.