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.
Related
I stumbled upon an unexpected behavior of a shared pointer I'm using.
The shared pointer implements reference counting and detaches (e.g. makes a copy of), if neccessary, the contained instance on non-const usage.
To achieve this, for each getter function the smart pointer has a const and a non-const version, for example: operator T *() and operator T const *() const.
Problem: Comparing the pointer value to nullptr leads to a detach.
Expected: I thought that the comparison operator would always invoke the const version.
Simplified example:
(This implementation doesn't have reference counting, but still shows the problem)
#include <iostream>
template<typename T>
class SharedPointer
{
public:
inline operator T *() { std::cout << "Detached"; return d; }
inline operator const T *() const { std::cout << "Not detached"; return d; }
inline T *data() { std::cout << "Detached"; return d; }
inline const T *data() const { std::cout << "Not detached"; return d; }
inline const T *constData() const { std::cout << "Not detached"; return d; }
SharedPointer(T *_d) : d(_d) { }
private:
T *d;
};
int main(int argc, char *argv[])
{
SharedPointer<int> testInst(new int(0));
bool eq;
std::cout << "nullptr == testInst: ";
eq = nullptr == testInst;
std::cout << std::endl;
// Output: nullptr == testInst: Detached
std::cout << "nullptr == testInst.data(): ";
eq = nullptr == testInst.data();
std::cout << std::endl;
// Output: nullptr == testInst.data(): Detached
std::cout << "nullptr == testInst.constData(): ";
eq = nullptr == testInst.constData();
std::cout << std::endl;
// Output: nullptr == testInst.constData(): Not detached
}
Question 1: Why is the non-const version of the functions called when it should be sufficient to call the const version?
Question 2: Why can the non-const version be called anyways? Doesn't the comparison operator (especially comparing to the immutable nullptr) always operate on const references?
For the record:
The shared pointer I'm using is Qt's QSharedDataPointer holding a QSharedData-derived instance, but this question is not Qt-specific.
Edit:
In my understanding, nullptr == testInst would invoke
bool operator==(T const* a, T const* b)
(Because why should I compare non-const pointers?)
which should invoke:
inline operator const T *() const
Further questions:
Why isn't it the default to use the const operator?
Is this because a function cannot be selected by the type of the return value alone?
=> This is answered in Calling a const function rather than its non-const version
So this question boils down to:
Why doesn't the default implementation of the comparison operator take the arguments as const refs and then call the const functions?
Can you maybe cite a c++ reference?
When there exists an overload on const and non-const, the compiler will always call non-const version if the object you're using is non-const. Otherwise, when would the non-const version ever be invoked?
If you want to explicitly use the const versions, invoke them through a const reference:
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
In the context of Qt's QSharedDataPointer, you can also use the constData function explicitly whenever you need a pointer.
For the intended usage of QSharedDataPointer, this behavior is not usually a problem. It is meant to be a member of a facade class, and thus used only from its member functions. Those member functions that don't need modification (and thus don't need detaching) are expected to be const themselves, making the member access to the pointer be in a const context and thus not detach.
Edit to answer the edit:
In my understanding, nullptr == testInst would invoke
bool operator==(T const* a, T const* b)
This understanding is incorrect. Overload resolutions for operators is rather complex, with a big set of proxy signatures for the built-in version of the operator taking part in the resolution. This process is described in [over.match.oper] and [over.built] in the standard.
Specifically, the relevant built-in candidates for equality are defined in [over.built]p16 and 17. These rules say that for every pointer type T, an operator ==(T, T) exists. Now, both int* and const int* are pointer types, so the two relevant signatures are operator ==(int*, int*) and operator ==(const int*, const int*). (There's also operator ==(std::nullptr_t, std::nullptr_t), but it won't be selected.)
To distinguish between the two overloads, the compiler has to compare conversion sequences. For the first argument, nullptr_t -> int* and nullptr_t -> const int* are both identical; they are pointer conversions. Adding the const to one of the pointers is subsumed. (See [conv.ptr].) For the second argument, the conversions are SharedPointer<int> -> int* and SharedPointer<int> -> const int*, respectively. The first of these is a user-defined conversion, invoking operator int*(), with no further conversions necessary. The second is a user-defined conversion, invoking operator const int*() const, which necessitates a qualification conversion first in order to call the const version. Therefore, the non-const version is preferred.
Maybe this code will allow you to understand what happens:
class X {
public:
operator int * () { std::cout << "1\n"; return nullptr; }
operator const int * () { std::cout << "2\n"; return nullptr; }
operator int * () const { std::cout << "3\n"; return nullptr; }
operator const int * () const { std::cout << "4\n"; return nullptr; }
};
int main() {
X x;
const X & rcx = x;
int* pi1 = x;
const int* pi2 = x;
int* pi3 = rcx;
const int* pi4 = rcx;
}
The output is
1
2
3
4
If the const object (or reference to it) is casted, the const cast operator (case 3 and 4) is choosen, and vice versa.
This is because of how the expression testInst == nullptr is resolved:
Let's look at the types:
testInst is of type SharedPointer<int>.
nullptr is (for the sake of simplification) of type T* or void*, depending on the use case.
So the expression reads SharedPointer<int> == int*.
We need to have equal types to invoke a comparison operator. There are two possibilities:
Resolve to int* == int*.
This involves a call to operator int *() or operator int const *() const.
[citation needed]
Resolve to SharedPointer<int> == SharedPointer<int>
This involves a call to SharedPointer(nullptr).
Because the second option would create a new object, and the first one doesn't, the first option is the better match. [citation needed]
Now before resolving bool operator==(int [const] *a, int [const] *b) (the [const] is irrelevant), testInst must be converted to int*.
This involves a call to the conversion operator int *() or operator int const *() const.
Here, the non-const version will be called because testInst is not const. [citation needed]
I created a suggestion to add comparison operators for T* to QSharedDataPointer<T> at Qt Bugs: https://bugreports.qt.io/browse/QTBUG-66946
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.
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.
I have this code:
template <class T>
class Something
{
T val;
public:
inline Something() : val() {}
inline Something(T v) : val(v) {}
inline T& get() const { return val; }
inline Something& operator =(const Something& a) { val = a.val; return *this; }
};
typedef Something<int> IntSomething;
typedef Something<const int> ConstIntSomething;
class Other
{
public:
IntSomething some_function()
{
return IntSomething(42);
}
ConstIntSomething some_function() const
{
return ConstIntSomething(42);
}
};
void wtf_func()
{
Other o;
ConstIntSomething s;
s = o.some_function();
}
However, the compiler picks the wrong overload of Other::some_function() in wtf_func() (i.e. the non-const one). How can I fix this? Note that for certain reasons I cannot change the name of Other::some_function().
o is not const-qualified, so the non-const some_function is selected. If you want to select the const-qualified overload, you need to add the const qualifier to o:
Other o;
Other const& oref(o);
ConstIntSomething s;
s = oref.some_function();
When overload resolution occurs, the compiler only looks at the o.some_function() subexpression; it does not look at the context around the function call to decide to pick something else. Further, the return type of a member function is not considered during overload resolution.
Note that it may make more sense for IntSomething to be implicitly convertible to ConstIntSomething, either using an operator ConstIntSomething() overload in IntSomething (less good) or using a non-explicit ConstIntSomething(IntSomething const&) constructor in ConstIntSomething (more good).
It doesn't pick the wrong overload; const-ness is resolved by whether this is const or not. In your case, o is non-const, so the non-const overload is picked.
You can hack this by creating a const-reference to o, e.g.:
const Other &o2 = o;
s = o2.some_function();
But really, you should probably be considering your overloads in Something. For instance, you can't currently do this:
IntSomething x;
ConstIntSomething y;
y = x;
which doesn't sound correct. Why shouldn't you be allowed to take a const ref to a non-const ref?
Your object o needs to be const object for a const function to be called on it. Otherwise the compiler rightly picks up the non const version of the function.
The compiler picks the overload to use based on the constness of the object that will become this. You can make it call the desired version with static_cast: s = static_cast<const Other&>(o.some_function());
You might also want to copy the new behaviour found in the containers of the C++0x standard library. Containers such as vector now have members cbegin() and cend() that return a const_iterator whether the container is const or not unlike begin() and end()
class Other {
// Rest of other
public:
// No overload for non-const
// Even if called with a non const Other, since this member is marked
// const, this will be of type Other const * in all cases and will call
// the const qualified overload of some_function.
ConstIntSomething csome_function() const
{
return some_function();
}
};
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.