Why have two functions for operator overload? - c++

I'm currently writing a double linked list using dynamic memory in C++ for one of my classes. I've already got the code written, I just had a question.
Our professor required us to write both an
int& operator[](int position)
AND an
int operator[](int position) const
function.
What was the point in doing two functions for the same operator? I'm sure there's some kind of logic behind it, haha. Is it just so I can do a = arr[i] and also do arr[i] = a?

If you have a const list object, you should only be able to read values from it. If you have a modifiable one, you should be able to both read and write values. The first is done with the const version, which will be called for a const object.

When your object is const, you can only use the const version of the operator, which by virtue of being const doesn't allow modification of your object or returning non-cons pointers/references to internal members. So when your object is non-const you need a non-const operator that allows modifications so you can write foo[i]=n

1) The const variant is for reading, e.g. when you assign a value to a variable. The compiler will give you an error when trying to modify the list value at the specified index. Example for const is a = arr[i] as you wrote.
2) The non-const variant is usually used when you actually set the value at the certain index of the list. Although you could use this variant for getting a value as well. However, then the compiler would not raise an error when your intention is to just read and your code would accidentally write. That could lead to subtle bugs which you can avoid by using const for such cases. Example for non-const is arr[i] = a as you wrote.
If you have both versions present, the non-const version version will be called for reading when you execute the indexing on a non-const object.

Is it just so I can do a = arr[i] and also do arr[i] = a?
Yes, that's what it's for.
if you have a const or a non-const list then you can perform the a = arr[i] assignment;
the version of the operator that returns an int& is so that you can perform arr[i] = a, which of course requires a non-const list.

Its just if you have a const variable of your object, lets call it A const a; then you are only allowed to call const functions, like your const overload of operator [] (...) const, which only provides reading. You are not allowed to call the non const operator [] (...) in this case.

Where you have a doubly-linked list object that is defined with the const qualifier (commonly called "a const object") or referred to via a pointer-to-const or a reference-to-const, you want to be enable reading but not writing. The const-qualified operator does that.
Where you have an object that is defined without the const qualifier, or referred to via a pointer-to-non-const or a reference-to-non-const, you want to enable both reading and writing. The non-const-qualified operator does that.
So, the reason you bother writing two overloads is to implement both behaviours.
If you only wanted reading, regardless of constness, then you would only write the const version because const-qualified member functions can be used to interact with both cases, whereas non-const-qualified member functions can only be used on the names of non-const-qualified objects (or via a pointer-to-non-const or a reference-to-non-const).
If you only wanted operator[] to apply to non-const objects then you would only write the non-const-qualified version. This might seem strange, since you don't expect operator[] to need the object to be modifiable, but as it happens this is exactly what std::map does with operator[].

Related

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

Quite new to C++. I have seen people usually pass objects by reference in operator overloading. Well, I can't figure out when it is really necessary. As in the code below, if I remove ampersand in declaration of object c1 and c2 in operator+, still I'll get the same result. Is there any reason to pass-by-reference in this case when we do not want to modify c1 or c2?
#include <iostream>
class Keys
{
private:
int m_nKeys;
public:
Keys(int nKeys) { m_nKeys = nKeys; }
friend Keys operator+(const Keys &c1, const Keys &c2);
int GetKeys() { return m_nKeys; }
};
Keys operator+(const Keys &c1, const Keys &c2)
{
return Keys(c1.m_nKeys + c2.m_nKeys);
}
int main()
{
Keys cKeys1(6);
Keys cKeys2(8);
Keys cKeysSum = cKeys1 + cKeys2;
std::cout << "There are " << cKeysSum.GetKeys() << " Keys." << std::endl;
system("PAUSE");
return 0;
}
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:
the parameter just is not cheap to copy (e.g. for ints, float, etc. just
don't bother: passing by value is just fine)
the parameter is observed in the function/operator implementation
(i.e. it's an input read-only parameter)
If you pass by reference then there is no copy of the object made, which for more complicated classes could greatly improve performance.
In this case the performance cost may be marginal, and it's conceivable the compiler could optimise it all out, but it's still worth doing. Later the Keys class may change into something more complex.
Advantages of passing by reference:
It allows us to have the function change the value of the argument, which is sometimes useful.
Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
We can pass by const reference to avoid unintentional changes.
We can return multiple values from a function.
Disadvantages of passing by reference:
Because a non-const reference can not be made to a literal or an expression, reference arguments must be normal variables.
It can be hard to tell whether a parameter passed by reference is meant to be input, output, or both.
It’s impossible to tell from the function call that the argument may change. An argument passed by value and passed by reference looks the same. We can only tell whether an argument is passed by value or reference by looking at the function declaration. This can lead to situations where the programmer does not realize a function will change the value of the argument.
Because references are typically implemented by C++ using pointers, and dereferencing a pointer is slower than accessing it directly, accessing values passed by reference is slower than accessing values passed by value.
You can read the below:
http://www.cs.fsu.edu/~myers/c++/notes/references.html
Consider a vector of long having 10 million entries in it. If you prototype a function like:
void foo(vector<long> vl)
{
}
It will cause assignment-operator (or copy-constructor) of vector<long> - and that would need to copy all those 10m elements. Later destructor for this temporary object (vl) would de-allocate memory and perform other cleanup. It will definitely impact performance
There are classes, specially around synchronization providers (critical sections etc.), and some smart pointer classes that prevent copy-constructor and/or assignment-operators - so that assignment or object creation doesn't happen by mistake. Though move-constructor or move-assignment-operator may be implemented.

C++: Why is const_cast evil?

I keep hearing this statement, while I can't really find the reason why const_cast is evil.
In the following example:
template <typename T>
void OscillatorToFieldTransformer<T>::setOscillator(const SysOscillatorBase<T> &src)
{
oscillatorSrc = const_cast<SysOscillatorBase<T>*>(&src);
}
I'm using a reference, and by using const, I'm protecting my reference from being changed. On the other hand, if I don't use const_cast, the code won't compile. Why would const_cast be bad here?
The same applies to the following example:
template <typename T>
void SysSystemBase<T>::addOscillator(const SysOscillatorBase<T> &src)
{
bool alreadyThere = 0;
for(unsigned long i = 0; i < oscillators.size(); i++)
{
if(&src == oscillators[i])
{
alreadyThere = 1;
break;
}
}
if(!alreadyThere)
{
oscillators.push_back(const_cast<SysOscillatorBase<T>*>(&src));
}
}
Please provide me some examples, in which I can see how it's a bad idea/unprofessional to use a const_cast.
Thank you for any efforts :)
Because you're thwarting the purpose of const, which is to keep you from modifying the argument. So if you cast away the constness of something, it's pointless and bloating your code, and it lets you break promises that you made to the user of the function that you won't modify the argument.
In addition, using const_cast can cause undefined behaviour. Consider this code:
SysOscillatorBase<int> src;
const SysOscillatorBase<int> src2;
...
aFieldTransformer.setOscillator(src);
aFieldTransformer.setOscillator(src2);
In the first call, all is well. You can cast away the constness of an object that is not really const and modify it fine. However, in the second call, in setOscillator you are casting away the constness of a truly const object. If you ever happen to modify that object in there anywhere, you are causing undefined behaviour by modifying an object that really is const. Since you can't tell whether an object marked const is really const where it was declared, you should just never use const_cast unless you are sure you'll never ever mutate the object ever. And if you won't, what's the point?
In your example code, you're storing a non-const pointer to an object that might be const, which indicates you intend to mutate the object (else why not just store a pointer to const?). That might cause undefined behaviour.
Also, doing it that way lets people pass a temporary to your function:
blah.setOscillator(SysOscillatorBase<int>()); // compiles
And then you're storing a pointer to a temporary which will be invalid when the function returns1. You don't have this problem if you take a non-const reference.
On the other hand, if I don't use const_cast, the code won't compile.
Then change your code, don't add a cast to make it work. The compiler is not compiling it for a reason. Now that you know the reasons, you can make your vector hold pointers to const instead of casting a square hole into a round one to fit your peg.
So, all around, it would be better to just have your method accept a non-const reference instead, and using const_cast is almost never a good idea.
1 Actually when the expression in which the function was called ends.
by using const, I'm protecting my reference from being changed
References can't be changed, once initialized they always refer to the same object. A reference being const means the object it refers to cannot be changed. But const_cast undoes that assertion and allows the object to be changed after all.
On the other hand, if I don't use const_cast, the code won't compile.
This isn't a justification for anything. C++ refuses to compile code that may allow a const object to be changed because that is the meaning of const. Such a program would be incorrect. const_cast is a means of compiling incorrect programs — that is the problem.
For example, in your program, it looks like you have an object
std::vector< SysOscillatorBase<T> * > oscillators
Consider this:
Oscillator o; // Create this object and obtain ownership
addOscillator( o ); // cannot modify o because argument is const
// ... other code ...
oscillators.back()->setFrequency( 3000 ); // woops, changed someone else's osc.
Passing an object by const reference means not only that the called function can't change it, but that the function can't pass it to someone else who can change it. const_cast violates that.
The strength of C++ is that it provides tools to guarantee things about ownership and value semantics. When you disable those tools to make the program compile, it enables bugs. No good programmer finds that acceptable.
As a solution to this particular problem, it looks likely that the vector (or whatever container you're using) should store the objects by value, not pointer. Then addOscillator can accept a const reference and yet the stored objects are modifiable. Furthermore, the container then owns the objects and ensures they are safely deleted, with no work on your part.
The use of const_cast for any reason other than adapting to (old) libraries where the interfaces have non-const pointers/references but the implementations don't modify the arguments is wrong and dangerous.
The reason that it is wrong is because when your interface takes a reference or pointer to a constant object you are promising not to change the object. Other code might depend on you not modifying the object. Consider for example, a type that holds an expensive to copy member, and that together with that it holds some other invariants.
Consider a vector<double> and a precomputed average value, the *average is updated whenever a new element is added through the class interface as it is cheap to update then, and if it is requested often there is no need to recompute it from the data every time. Because the vector is expensive to copy, but read access might be needed the type could offer a cheap accessor that returns a std::vector<double> const & for user code to check values already in the container. Now, if user code casts away the const-ness of the reference and updates the vector, the invariant that the class holds the average is broken and the behavior of your program becomes incorrect.
It is also dangerous because you have no guarantee that the object that you are passed is actually modifiable or not. Consider a simple function that takes a C null terminated string and converts that to uppercase, simple enough:
void upper_case( char * p ) {
while (*p) {
*p = ::to_upper(*p);
++p;
}
}
Now lets assume that you decide to change the interface to take a const char*, and the implementation to remove the const. User code that worked with the older version will also work with the new version, but some code that would be flagged as an error in the old version will not be detected at compile time now. Consider that someone decided to do something as stupid as upper_case( typeid(int).name() ). Now the problem is that the result of typeid is not just a constant reference to a modifiable object, but rather a reference to a constant object. The compiler is free to store the type_info object in a read-only segment and the loader to load it in a read-only page of memory. Attempting to change it will crash your program.
Note that in both cases, you cannot know from the context of the const_cast whether extra invariants are maintained (case 1) or even if the object is actually constant (case 2).
On the opposite end, the reason for const_cast to exist was adapting to old C code that did not support the const keyword. For some time functions like strlen would take a char*, even though it is known and documented that the function will not modify the object. In that case it is safe to use const_cast to adapt the type, not to change the const-ness. Note that C has support for const for a very long time already, and const_cast has lesser proper uses.
The const_cast would be bad because it allows you to break the contract specified by the method, i.e. "I shall not modify src". The caller expects the method to stick to that.
It's at least problematic. You have to distinguish two constnesses:
constness of the instantiated variable
This may result in physical constness, the data being placed in a read-only segment
constness of the reference parameter / pointer
This is a logical constness, only enforced by the compiler
You are allowed to cast away the const only if it's not physically const, and you can't determine that from the parameter.
In addition, it's a "smell" that some parts of your code are const-correct, and others aren't. This is sometimes unavoidable.
In your first example, you assign a const reference to what I assume is a non-const pointer. This would allow you to modify the original object, which requires at least a const cast. To illustrate:
SysOscillatorBase<int> a;
const SysOscillatorBase<int> b;
obj.setOscillator(a); // ok, a --> const a --> casting away the const
obj.setOscilaltor(b); // NOT OK: casting away the const-ness of a const instance
Same applies to your second example.
, while I can't really find the reason why const_cast is evil.
It is not, when used responsibily and when you know what you're doing. (Or do you seriously copy-paste code for all those methods that differ only by their const modifier?)
However, the problem with const_cast is that it can trigger undefined behavior if you use it on variable that originally was const. I.e. if you declare const variable, then const_cast it and attempt to modify it. And undefined behavior is not a good thing.
Your example contains precisely this situation: possibly const variable converted into non-const. To avoid the problem store either const SysOscillatorBase<T>*(const pointer) or SysOscillatorBase<T> (copy) in your object list, or pass reference instead of const reference.
You are violating a coding contract. Marking a value as const is saying you can use this value but never change it. const_cast breaks this promise and can create unexpected behaviour .
In the examples you give, it seems your code is not quite right. oscillatorSrc should probably be a const pointer, although if you really do need to change the value then you should not pass it in as a const at all.
Basicly const promises you and the compiler that you will not change the value. The only time you should use when you use a C library function (where const didn't exist), that is known not to change the value.
bool compareThatShouldntChangeValue(const int* a, const int* b){
int * c = const_cast<int*>(a);
*c = 7;
return a == b;
}
int main(){
if(compareThatShouldntChangeValue(1, 7)){
doSomething();
}
}
You probably need to define you container as containing const objects
template <typename T> struct Foo {
typedef std::vector<SysOscillator<T> const *> ossilator_vector;
}
Foo::ossilator_vector<int> oscillators;
// This will compile
SysOscillator<int> const * x = new SysOscillator<int>();
oscillators.push_back(x);
// This will not
SysOscillator<int> * x = new SysOscillator<int>();
oscillators.push_back(x);
That being said if you have no control over the typedef for the container maybe it
is ok to const_cast at the interface between your code and the library.

Doom 3 source: why does a class representing 2-vectors overload the subscript operator twice?

I thought I'd try beefing up my C++ and OpenGL by looking at the recently-released Doom 3 source. Much learned so far, but I've hit a wall. The class detailed here has methods
float operator[] (int index) const
and
float & operator[] (int index)
whose bodies both read
return ( &x )[ index ];
where x is one of the class' two data members (the other being y; this class is for 2-vectors).
While I can understand the syntax of each version's header/prototype, I don't get why they're both present.
const seems to appear (or not appear, as preferred) only to distinguish the headers sufficiently to allow compilation. (That is, remove const and VS2010 refuses to compile, similarly if both headers end in const.)
And why return a ref to a float? None of the class' seven other float-type methods do this, so I'm guessing efficiency isn't a factor (tho' maybe this operator's called vastly more often than the others).
Appreciate any insight as to what's going on here...
This is a common idiom (known as "const overloading"). See the C++ FAQ: http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.12.
The ambiguity is resolved by whether *this is const or not. On a const object, the const overload is called, in which case it acts in a read-only style. On a non-const object, the non-const is called, in which case it acts in a read/write style.
Note, crucially, that this is not a way of distinguishing between read and write accesses.
Think of them as a related pair of getter and setter methods for subscripted elements. The float & operator[](int index) is the setter version and allows you to use syntax like so:
theObject[anIndex] = 1.0;
This requires that theObject is available to you as a non-const object (or through an Object * or Object &).
In the case without const, you are using a reference because you want to change the value when calling the function. Eg setting a value: a[11] = 5.0;
The case with const is added because other functions can only be declared as const functions if all other functions that they call are also const functions.
And why return a ref to a float?
The reason to return a reference to a float is to allow the caller to modify that float. (By the way, efficiency wouldn't be a reason for this, since pointers tend to be at least as big as floats, so the extra indirection is pure cost.)
const at the end of a method signature indicates that it can safely be called on a const object/reference/expression. So, if v is an idVec2, then v[0] may be used as an lvalue (and v[0] = 3.0f will actually set v[0]), but if v is a const idVec2, then v[0] can only be used as an rvalue (meaning that v[0] = 3.0f is ilegal).

duplicating the same method with const?

In C++, I see this code.
public:
Ports& GetPorts();
const Ports& GetPorts() const;
Why is it necessary to have another method with const?
How can a compiler decide which method is called?
If you call x.GetPorts() and x is a non-const object, the first version will be called. If x is a const object, on the other hand, the second version will be called. That kind of code is saying "if the object is modifiable, allow the result of GetPorts() to be modified; if the object is const, don't allow the result to be modified." The compiler will prefer the first version if it matches; however, it will not be present if the object is const and so the second version will be used instead.
Because the first overload is not a const method you can't call it over temporaries and const objects. If you provide a const overload you essentially support const objects.
The compiler will use the const overload for const objects and the none-const overload for none-const objects.
It is usually not necessary to provide an overload if your function is const, because const functions are as safe as they get: they work for both const objects and none-const objects.
This is necessary only if you want to have different behavior for const and non-const objects. Otherwise the second version is enough. A design decision.

Should I return bool or const bool?

Which is better:
bool MyClass::someQuery() const;
const bool MyClass::someQuery() const;
I've been using 'const bool' since I'm sure I remember hearing it's "what the ints do" (for e.g. comparison operators) but I can't find evidence of that anywhere, mostly due to it being difficult to Google and Intellisense not helping out any ;) Can anyone confirm that?
To me returning const values (this isn't just about bools) makes more sense; it'll prevent temporaries being modified, which is almost always going to be a programmer mistake. I just want something to back that up so I can extol returning const values to my colleagues :)
This is the case when const adds no value but inflates the code and makes the reader think more. What's the point of this const? The caller can copy the value into some non-const variable and do whatever he wants with it anyway.
So you know it's right, you're just after the Voice of Authority?
Preventing accidental modification of temporaries is very valuable. In general, you should declare as many things as you possibly can const, it protects you from a variety of accidents and gives the optimiser useful hints.
D'you have a copy of Scott Meyers' "Effective C++" around? Point them at Item 3 (page 18 in the third edition) ;)
It gives the example of
class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs );
if( (a * b) = c ) // declaring operator *'s return value const causes error to be caught by compiler
Note that if((a*b) = c) won't compile for built-in types anyway, so it is very relevant here whether we're talking built-in types (your question asks for bool) or user-defined types.
For built-in types it makes no sense at all, so it shouldn't be used. And for user-defined types, I'm in jalf's camp: What if the caller wants to modify the returned object?
I'm not convinced that if((a*b) = c) is such a good argument for returning const user-defined types, since I can't remember the last time I've seen a compiler not warn about this.
To be a little more specific, only "objects" can be const. The C++ standard's definition of "object" includes everything an lvalue refers to ("has a name") and class-type temporaries. A boolean return value is an rvalue of a non-class type which is why a standards-compliant compiler will just ignore "const" in this case. As others said already, it's useless in this context.
When you returning a refernce to a member variable it makes sense to make it const. Here you are returning a copy, hence there is no need of const.
The const modifier is only used for return types that are returned by reference (either as reference const SomeObject& or via a pointer const SomeObject*), so the caller won't be able to modify the object via the reference/pointer. Primitive types are returned by value, which means that the caller receives a copy of the the object, not the object itself.
Therefore, const is not really appropriate for returned value types. Since the copy is outside of the control of the called function, the called function should not dictate to the caller that it cannot be changed.
This is an ancient post, but I think it's worth mentioning there is a potential corner case here since C++11. While, as stated by others, it will make no difference whether you use const bool or bool as return type in most cases, if you are using C++11 decltype and associates, e.g. result_of, you could declare a variable with the same type as the returning value of some function, and so the const would actually have an effect in this case.
It completely doesn't matter. Therefore, the consensus is to return just bool.
The reason that it doesn't matter is that you can't call non-const member functions anyway; bool is not a class or struct.
As bool is going to be copied, it's the same, to put const or not. Plus you'll may have some compil problems.
const return type
SUMMARY:
The value of a return type that is
declared const cannot be changed. This
is especially usefull when giving a
reference to a class’s internals, but
can also prevent rarer errors.
const bool func();
bool f = func();
0 errors, 0 warnings. What have you accomplished other than unnecessary code inflation?