If I have a pointer to an object that has an overloaded subscript operator ([]) why can't I do this:
MyClass *a = new MyClass();
a[1];
but have to do this instead:
MyClass *a = new MyClass();
(*a)[1];
It's because you can't overload operators for a pointer type; you can only overload an operator where at least one of the parameters (operands) is of class type or enumeration type.
Thus, if you have a pointer to an object of some class type that overloads the subscript operator, you have to dereference that pointer in order to call its overloaded subscript operator.
In your example, a has type MyClass*; this is a pointer type, so the built-in operator[] for pointers is used. When you dereference the pointer and obtain a MyClass, you have a class-type object, so the overloaded operator[] is used.
Because a is type pointer to a MyClass and not a MyClass. Changing the language to support your desired use would make many other language semantics break.
You can get the syntactic result you want from:
struct foo {
int a[10];
int& operator [](int i) { return a[i]; }
};
main() {
foo *a = new foo();
foo &b = *a;
b[2] = 3;
}
Good news. You can also do...
a->operator[](1);
To add on to the preferred answer, think of operator overloading as overloading functions.
When overloading member function of a class, you remember that the pointer is not of that class type.
Simply put, with a[1] the a pointer is treated as memory containing array, and you're trying to access the 2nd element in the array (which doesn't exist).
The (*a)[1] forces to first get the actual object at the pointer location, (*a), and then call the [] operator on it.
Related
This question already has answers here:
Difference between 'new operator' and 'operator new'?
(8 answers)
Closed 3 years ago.
We know that void* holds no information regarding the actual type of the data it points to. However, from cppreference on new and new[] we know that those operators return void*. How come, then, given:
auto x = new int{};
knowing that the new operator should return void*, x is deduced to be of a type int*, not void*?
Consider yet another example:
struct foo {
static void* operator new(std::size_t n) {
std::cout << "new called!\n";
return ::new char[n];
}
};
let's add some TypeDisplayer:
template <typename T>
struct TD;
and the test code:
int main() {
auto x = new foo{};
TD<decltype(x)>{};
}
The code fails to compile with error indicating that decltype(x) is foo*. If we comment out the last line of main, we will know that our, void*-returning operator will be called due to new called! being printed.
It's easy to confuse the new keyword with the operator new, but they're two different things. When you write a memory allocation function, its name is operator new and it does, as you say, return void*. But you don't ordinarily call that operator directly; instead, you create a new object with the new keyword. The compiler understands that keyword; it calls operator new to get memory for the object (or objects, for array new) that's being created and does whatever initialization is appropriate. The type of the result of that expression is a pointer to the type being created.
So, in the code
auto x = new int{};
the type of the expression new int{} is int*, so the deduced type for x is also int*.
I’m reading some code my teacher gave me and I don’t quite understand one specific line of code. The function returns an int&.
return (*(Vector *)this)[i];
this return statement is in an operator overload of “[]”. There is also another operator overload of [] that is defined in the base class of “this”. The base class is a defined class “Vector”. I don’t understand this line of code.
When in doubt, simplify.
First step:
return (*(Vector *)this)[i];
can be
Vector* ptr = (Vector*)this;
return (*ptr)[i];
Second step:
return (*ptr)[i];
can be
Vector& ref = *ptr;
return ref[i];
Both simplifications put together, the line
return (*(Vector *)this)[i];
is equivalent to
Vector* ptr = (Vector*)this;
Vector& ref = *ptr;
return ref[i];
When the member function is a const member function, this is of type Vector const* const.
The first line removes the const-ness of the object pointer.
The second line dereferences the pointer.
The last line returns the i-th element of the object.
I don’t understand this line of code.
return (*(Vector *)this)[i];
Assuming T is a type, T* is the type "pointer to T". Thus given that Vector is a type, Vector* is a pointer to Vector.
this is a special name which is a pointer to the object argument of a member function.
(T)expr is an explicit type conversion. It performs one or combination of static cast, reinterpret cast or const cast on the expression, converting the value to the type T. In the expression (Vector *)this, the this pointer is converted to the type Vector*. Given that Vector is a base class, this is a static cast, and more specifically, an up cast because we are casting up in the inheritance hierarchy.
The unary operator * in *expr is the indirection operator. It indirects through the pointer (in this case) operand and results in an lvalue of the pointed object. Thus, *(Vector *)this will be an lvalue of type Vector that is the base class sub object of this.
expr[index] is the subscript operator. Thus, (*(Vector *)this)[i] invokes the subscript operator of the base class on this object.
Finally, return expr; statement jumps out of the function, and returns the value of the expression to the caller. Thus, return (*(Vector *)this)[i]; returns the result of the base class' subscript operator.
In smart pointer implementation, dereferencing operator and member selection operators are always defined as below.
T& operator* () const // dereferencing operator
{
return *(m_pRawPointer);
}
T* operator->() const // member selection operator
{
return m_pRowPointer;
}
I don't quite understand why the former is returned by reference, the latter is returned by pointer. Is it just to differentiate them or some other reasons?
To be more specific, can I make dereferencing operator returns by pointer, while the other one returns by reference?
why the former is returned by reference
So that the expression *thing gives an lvalue denoting an object of type T, just as it would if thing were a pointer.
the latter is returned by pointer
Because that's how the language is specified. Note that you never use the result of -> directly, but always in an expression of the form thing->member.
If thing is a class type, that's evaluated by calling operator->, then applying ->member to the result of that. To support that, it must return either a pointer, or another class type which also overloads operator->.
can I make dereferencing operator returns by pointer
Yes, but that would be rather confusing since it would behave differently to applying the same operator a pointer. You'd have to say **thing to access the T.
while the other one returns by reference
No, because that would break the language's built-in assumptions about how the overloaded operator should work, making it unusable.
The reason that the dereference operator returns by reference and the member selection operator returns by pointer is to line up the syntax of using a smart pointer with the syntax of using a raw pointer:
int* p = new int(42);
*p = 7;
std::unique_ptr<int> p(new int(42));
*p = 7;
You could absolutely make your dereference operator return anything you like:
struct IntPtr {
int* p;
int* operator*() { return p; }
};
But that would be pretty confusing for your users when they have to write:
IntPtr p{new int{42}};
**p = 7;
The arrow operator is a little different in that [over.ref]:
An expression x->m is interpreted as (x.operator->())->m
So you have to return something on which you can call ->m, otherwise you'll just get an error like (from gcc):
error: result of 'operator->()' yields non-pointer result
This question already has answers here:
-> usage in smart pointers
(2 answers)
Closed 9 years ago.
smart pointers like shared_ptr can be used like ordinary pointers with * and -> operator.
The books say that -> operator returns the pointer that shared_ptr stores. So you can use it to access the object this pointer points to. But I am confused here. Look at the code below.
class A
{
public:
A(int v = 20){val = v;}
int val;
}
A* p1 = new A;
std::cout<<p1->val; //This is common sense
boost::shared_ptr<A> p2(new A);
std::cout<<p2->val; //This is right
//My question is that p2-> returns the pointers of the object, then maybe another
//-> should be used?
//like (p2->)->val?
It's magic. Well, more like a special case. The standard says that
13.5.6 Class member access [over.ref]
1 operator-> shall be a non-static member function taking no parameters. It implements the class member access syntax that uses ->.
postfix-expression -> templateopt id-expression
postfix-expression -> pseudo-destructor-name
An expression x->mis interpreted as (x.operator->())->m for a class object x of type T if T::operator->()
exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3).
That is, operator-> is called again on the result of the overloaded operator. And if that one is overloaded too, it goes on recursively until a raw pointer is the result and the built-in operator-> is called.
That doesn't mean the result can't be any arbitrary type - it can be, but then you can only call it with the function call syntax:
struct X {
int operator->() { return 42; }
};
int main()
{
X x;
x.operator->(); // this is OK
}
Fortunately, designers of C++ realized this, and changed the semantics of the overloaded infix -> operator to mean that the pointer returned by your operator is plugged in on the left-hand side of the -> operator, and then the member is accessed as usual.
If you were to invoke the -> operator explicitly, you would need to insert an additional ->, though:
std::cout << p1.operator->()->val;
Demo on ideone.
The return value of operator -> is not the same as what p2->val returns. The return value of operator -> is a pointer that will be used to access the val member, so p2->val works as expected.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Overloading operator ->
Hi,
I've seen that operator->() is chained (re-applied) after it is evaluated, for example:
struct Bar
{
Bar() : m_str("Hello world!") {}
const string* operator->() const { return &m_str; }
string m_str;
};
struct Foo
{
const Bar& operator->() const { return m_bar; }
Bar m_bar;
};
int main()
{
Foo f;
cout << f->c_str() << endl;
return 0;
}
works pretty fine, which requires three operator->() to be evaluated - Foo::operator->(), Bar::operator->() and regular pointer resolution.
But it wont work with pointers in the middle - if Foo::operator->() returns pointer to Bar instead of reference, it wont compile. Same happens with auto_ptr<auto_ptr<string>> for example.
Is it specific to non-overloaded operator->() so it is only applied once and does not cause chaining?
Is it possible to make code below works without using (*ptr2)-> ...?
int main()
{
string s = "Hello world";
auto_ptr<string> ptr1(&s);
auto_ptr<auto_ptr<string> > ptr2(&ptr1);
cout << ptr1->c_str() << endl; // fine
cout << ptr2->c_str() << endl; // breaks compilation
}
Thanks!
C++98 standard §13.5.6/1 "Class member access":
An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator-> exists and if the operator is selected at the best match function by the overload resolution mechanism (13.3).
What this means in practice is that when x is a pointer, you don’t get chaining; you then just get the built-in operator-> (i.e. x->m with x a pointer translates to (*x).m).
But when x is an object of class type T, then you can get the chaining effect. Because then the interpretation as (x.operator->())->m can be that (x.operator->()) itself is an object of some class, say class U. Whence the second -> can be resolved as U::operator->, and so on, if the result of that again is a class type object…
Like, in your case, Foo::operator-> produces (a reference to) an object of class Bar, which does define an operator->.
But when operator-> returns a pointer, as e.g. std::auto_ptr<T>::operator-> does, then it's just the built-in operator-> that's used.
In passing, the chaining can be used to practically prevent someone from using delete inappropriately. std::auto_ptr does not do that. And I’ve never seen it done.
But there was once a long discussion thread over in [comp.lang.c++.moderated] about how to prevent inadvertent delete of the raw pointer managed by a smart pointer, and this was one possibility that was discussed.
Cheers & hth.
The reason your first example works is because you returned a reference instead of a pointer. That operator would normally be invalid except in the case that it is overloaded. Therefore, the compiler must execute the overloaded functions down the chain. However, in the case of auto_ptr you actually are returned a real pointer and the default operator -> is invoked for regular pointers.
Please see the Overloading operator -> question for more details.
No, it is not possible for it to work. If you could overload operator -> for string * you could make it work. But operator -> already has a definition for all pointer types. So, just like you can't overload + for primitive numeric types, you can't overload operator -> for any pointer type.
And even if you could, how could the compiler know when the recursion should end?