Same declaration with and without const, why? - c++

If I have
T& operator[](int i) const;
const T& operator[](int i) const;
how can I use one instead of another one? Why would one define these?

I don't think you can - they are ambiguous. Typically you'd want this:
T& operator[](int i); // note no trailing const
const T& operator[](int i) const;
The first overload is selected when your object is non-const - it's a better match than the second, and the second is selected when the instance is const - the first doesn't match at all.

The correct pair is this (I think your post has typo, as you seem to mean the following):
T& operator[](int i); //without const;
const T& operator[](int i) const;
As for your question, I would suggest you to define both of them, because you may end up using both in your code. Here is one example.
void f(X const &a, X & b)
{
int i = get_index();
std::cout << a[i] << std::endl; //this invokes const version
std::cout << b[i] << std::endl; //this invokes non-const version
}
In this code, a[i] invokes const-version of operator[], because a is a const object, which means it cannot invoke non-const version. However, in case of b[i], the C++ rules dictate that it should invoke non-const version, because the object b is non-const and there exists a non-const overload of operator[], so that is preffered. In the absence of non-const overload, even b[i] would invoke const version which is not desirable on many occasions, such as when you would like to write data at index i, as:
b[i] = item; //it must invoke non-const version in order
//to compile and work (properly).
Hope that helps.

Related

Why do I need twice the same function?

I am beginner in cpp language.
Recently I saw in a many classes declare twice the same function with a little different such as:
int& operator[](size_t i) {
assert(i<size());
return _buf[i];
}
const int& operator[](size_t i) const {
assert(i<size());
return _buf[i];
}
What is the different between the function? why I need the first one? and in which cases the first function will be work and in which cases the second function will be work?
On of those is const the other isnt. Let me put it in some context:
struct Foo{
int value = 0;
int& operator[](size_t i) {
std::cout << "non-const\n";
return value;
}
const int& operator[](size_t i) const {
std::cout << "const\n";
return value;
}
};
The const version will be called on const instances while the non-const will be called on non-const instances. E.g.
int main(){
Foo f;
int x = f[0];
f[0] = 3; // OK
const Foo g;
int x = g[0];
//g[0] = 3; // NOT OK
}
...will print
non-const
const
Indeed both methods should be the same and the major difference is the const version returning a const reference while the non-const returns a reference that allows to modify the value.
As you correctly observed, apart from the constness and the return type, the two are identical. To avoid duplicate code, sometimes it can make sense to use a small trick and write the const version in terms of the non-const:
const int& operator[](size_t i) const {
std::cout << "const\n";
return const_cast<Foo*>(this)->operator[](i);
}
See here for the full example.
Usually you don't want to let users change your objects somehow if they are marked as const.
It means that if you have a class which provides operator[], you don't want to let users change the internal state of objects of this class via operator[] if these objects are const.
That's why you have two overloads. If the object is const, then the version
const int& operator[](size_t i) const
is called. This version returns const int&, so you can't do any modification.
In opposite, if the object is not marked as const, then
int& operator[](size_t i)
is called and you are free to modify the internal state of the object via the reference returned.
The differences are the const keyword:
int& operator[](size_t i) { (1)
const int& operator[](size_t i) const { (2)
The first function return a reference to the object, which means that you can modify the object (for example by doing foo[0] = bar.
The second use the const keyword twice: const int& means that you return a const reference that you can't modify. The second const is here to specify that this function will not modify the object.
You need those two version because (1) is used when you want to modify an element of the collection and (2) is used on const object:
you can do this:
void foo(std::vector<int> const& v) {
int j = v[0];
}
because vector as an operator that look like (2)
The first overload states that the subscript operator can modify internals of the class instance, the later states that internals of the class instance are read-only and thus, can't be modified.
Effectively, it means that this pointer points to either const or non-const object.
Previously:
You tagged your question with C which is not correct, as C does not offer any class member functions and thus AFAIK, const after the global function declaration is illegal.
It means your class is providing the support for two things,
Non Const Object
Const Object
int& operator[](size_t i) will be called for Non const object, because there is no const qualifier at the end.
const int& operator[](size_t i) const will be called for const object, because there is const qualifier at the end.

How does compiler determine which function to use?

As we know compiler determines the function by the overloaded function signature (like parameter types), but how does this one work:
v[i] = 1;
When compiler looks at these two overloaded functions:
const T& operator[](size_t i) const;
T& operator[](size_t i);
How does it determine which one to use? Does the compiler tries to use 1st one, and finds out it does not work, then it tries to use the second one?
If the object is non-const, the non-const version of the function is invoked (if it is available), else const version is invoked. Now see which is which:
const T& operator[](size_t i) const; //CONST MEMBER FUNCTION
T& operator[](size_t i); //NON-CONST MEMBER FUNCTION
An example,
void f(std::vector<int> const &v1, std::vector<int> & v2)
{
std::cout << v1[0] << std::endl; //invokes const version
std::cout << v2[0] << std::endl; //invokes non-const version
}
Now when you write:
v[i] = 1;
If I don't assume v to be std::vector, then it depends on the const-ness of the v. If v is const, then const-version will be invoked, else non-const version will be invoked (if it is available, else v will convert into const object and then const function will be invoked).
The non-const member function cannot be called on a const object.
Hence, in order to be practically useful, the rules have to be that it's called on a non-const object, and that conversely, the const member function is called on a const object.
And that's what the rules are.

Definition of operators

I am trying to define some operators.
I do it according to this documentation:
http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
Is there an operator that should be defined twice?
I think that it's the index operator. am I right? I defined it such as:
int operator [] (const int power) const{
// here there is some code
}
Assuming I implement it correctly, what's about the operator that should have be defined twice?
Does it support the next things?
a[1] = 3;
cout << a[1]; // I defined the << operator
any help appreciated!
I think that it's the index operator. am I right?
Almost. It is called the subscript operator, and it must accept one single argument. Your operator accepts two, and that makes your code illegal.
Does it support the next things?
Supposing you have a properly written operator [] (without knowing some context from the logic of your application I cannot tell how to write one), then both the instructions you mention should be supported.
However, in order for this:
a[1] = 3;
To be legal (if a fundamental type is returned), operator [] should return an lvalue reference - therefore, int& and not int. Of course, this means the object to which the lvalue reference is bound cannot be a local object or a temporary, because that would mean returning a dangling reference.
int& operator [] (const int power) { // <== The function cannot be "const" if you
// ^ // are returning a non-const lvalue ref
// to a data member or element of a data
// member array
// here there is some code
}
You may also want a const version of the subscript operator:
int operator [] (const int power) const {
// No need to use a int const& ^^^^^
// here, that is basically the This member function can be const, since it is
// same as returning an int by neither modifying the object on which it is
// value invoked, nor returns any non-const lvalue ref
to a data member or an element of data member
// here there is some code
}
You may want to have both a const and a non-const versions of the operator:
int operator[](int index) const { ... }
int& operator[](int index) { ... }
This will permit both usages given in your example. It will also permit the second usage even if a is const.
To support assignment there are two options
The indexing operator [] returns a reference
The indexing operator [] returns a proxy object that implements assignment
The first case is the simplest, but doesn't allow you to distinguish between read and write operations. The second method is a bit more complex but allows more control:
An example of approach (2) is the following
struct MyArray {
std::vector<int> v;
MyArray(int n) : v(n) {}
struct ItemRef {
MyArray& a;
int index;
ItemRef(MyArray& a, int index)
: a(a), index(index)
{}
int operator=(int x) {
printf("Writing to element %i\n", index);
a.v[index] = x;
return x;
}
operator int() {
printf("Reading element %i\n", index);
return a.v[index];
}
};
ItemRef operator[](int index) {
if (index < 0 || index >= int(v.size()))
throw std::runtime_error("Invalid index");
return ItemRef(*this, index);
};
};

Call nonconst member version from const [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I remove code duplication between similar const and non-const member functions?
i have two members
A &B::GetA (int i)
{
return *(m_C[m_AtoC[i]]);
}
const A &B::GetA (int i) const
{
return *(m_C[m_AtoC[i]]);
}
for now i just duplicate code, but may be there exists nice way to do it. I certanly dont want to deal with type cast from const to non-const.
EDIT: So i want to call one member frm another to avoid code duplication.
[Note the correction; sorry for getting it the wrong way round initially.]
This is an appropriate situation for using a const_cast, and it allows you to deduplicate code by forwarding the call from the non-const function to the corresponding const function:
A & B::GetA(int index)
{
return const_cast<A &>(static_cast<B const *>(this)->GetA(index));
}
It's important to note that this should only be done in one direction: You can implement the non-const method in terms of the constant one, but not the other way round: The constant call to GetA() gets a constant reference to the object in question, but since we have additional information that it's actually OK to mutate the object, we can safely cast away the constness from the result.
There's even a chapter on precisely this technique in Scott Meyer's Effective C++.
You could do something like:
class B {
public:
A& GetA (int index) { return GetA_impl(index); }
const A& GetA (int index) const { return GetA_impl(index); }
private:
A& GetA_impl (int index) const { return *(m_C[m_AtoC[i]]); }
};
I'm not sure it's really worth the effort in this case, but this can be useful if the potentially duplicated code gets more complicated.
You can avoid const_cast with a little template metaprogramming.
// This meta function returns a const U if T is const, otherwise returns just U.
template <typename T, typename U>
make_same_const<T, U>
{
typedef typename std::conditional<
std::is_const<T>::value,
typename std::add_const<U>::type,
U
>::type type;
};
// Generic version of a function. Constness of return type depends on
// constness of T.
// This is a static member template of B.
template <typename T>
make_same_const<T, A>::type& B::GetA(T& obj, int i)
{
return *(obj.m_C[obj.m_AtoC[i]]);
}
A& B::GetA(int i) { return B::GetA(*this, i); }
A const& B::GetA(int i) const { return B::GetA(*this, i); }
IMO this isn't enough code (or complexity) to be worth de-duplicating.
As you can see in both the const_cast-based solutions, the cast expression is actually longer than the original code.
If you have a longer or more complex expression you're really worried about, though, please show it.
Assuming the bodies of GetA() and GetA() const are identical (which means GetA() doesn't modify *this), you can safely use one const_cast to implement the const version using the non-const one:
const A& B::GetA() const {
return const_cast<B*>(this)->GetA();
}
The non-const GetA() doesn't modify the object, so calling it on a const B object is not undefined. The non-const reference returned by non-const GetA() is converted to a const& before being returned out of GetA() const, so there's no danger of undefined behaviour there either.
How about
const A &B::GetA (int index) const
{
return *(const_cast<B*>(this)->GetA(index));
}

const overloaded operator[] function and its invocation

I define two versions of overloaded operator[] function in a class array. ptr is a pointer to first element of the array object.
int& array::operator[] (int sub) {
return ptr[sub];
}
and
int array::operator[] (int sub) const {
return ptr[sub];
}
Now, if I define a const object integer1 the second function can only be called..... but if I make a non-const object and then invoke as below:
cout << "3rd value is" << integer1[2];
which function is called here?
In your second example, the non-const version will be called, because no conversion is required, and a call that requires no conversion is a better match than one that requires a conversion.
Ultimately, however, you have a basic problem here: what you really want is behavior that changes depending on whether you're using your object as an rvalue or an lvalue, and const doesn't really do that. To make it work correctly, you normally want to return a proxy object, and overload operator= and operator T for the proxy object:
template <class T>
class myarray {
T *ptr;
class proxy {
T &val;
proxy &operator=(proxy const &p); // assignment not allowed.
public:
proxy(T &t) : val(t) {}
operator T() const { return val; }
proxy &operator=(T const&t) { val = t; return *this; }
};
proxy const operator[](int sub) const { return proxy(ptr[sub]); }
proxy operator[](int sub) { return proxy(ptr[sub]); }
// obviously other stuff like ctors needed.
};
Now we get sane behavior -- when/if our array<int> (or whatever type) is const, our operator[] const will be used, and it'll give a const proxy. Since its assignment operators are not const, attempting to use them will fail (won't compile).
OTOH, if the original array<int> was not const, we'll get a non-const proxy, in which case we can use both operator T and operator=, and be able to both read and write the value in the array<int>.
Your const version should return const int& not int, so that the semantics are just the same between the two functions.
Once you've done that, it doesn't matter which one is used. If the const version has to be used because your object has a const context, then it will be... and it won't matter as you're not trying to modify anything. Otherwise, it'll use the non-const version... but with just the same effect.