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
Related
I'm learning C++ and pointers and I thought I understood pointers until I saw this.
On one side the asterix(*) operator is dereferecing, which means it returns the value in the address the value is pointing to, and that the ampersand (&) operator is the opposite, and returns the address of where the value is stored in memory.
Reading now about assignment overloading, it says "we return *this because we want to return a reference to the object". Though from what I read *this actually returns the value of this, and actually &this logically should be returned if we want to return a reference to the object.
How does this add up? I guess I'm missing something here because I didn't find this question asked elsewhere, but the explanation seems like the complete opposite of what should be, regarding the logic of * to dereference, & get a reference.
For example here:
struct A {
A& operator=(const A&) {
cout << "A::operator=(const A&)" << endl;
return *this;
}
};
this is a pointer that keeps the address of the current object. So dereferencing the pointer like *this you will get the lvalue of the current object itself. And the return type of the copy assignment operator of the presented class is A&. So returning the expression *this you are returning a reference to the current object.
According to the C++ 17 Standard (8.1.2 This)
1 The keyword this names a pointer to the object for which a
non-static member function (12.2.2.1) is invoked or a non-static data
member’s initializer (12.2) is evaluated.
Consider the following code snippet as an simplified example.
int x = 10;
int *this_x = &x;
Now to return a reference to the object you need to use the expression *this_x as for example
std::cout << *this_x << '\n';
& has multiple meanings depending on the context. In C and used alone, I can either be a bitwise AND operator or the address of something referenced by a symbol.
In C++, after a type name, it also means that what follows is a reference to an object of this type.
This means that is you enter :
int a = 0;
int & b = a;
… b will become de facto an alias of a.
In your example, operator= is made to return an object of type A (not a pointer onto it). This will be seen this way by uppers functions, but what will actually be returned is an existing object, more specifically the instance of the class of which this member function has been called.
Yes, *this is (the value of?) the current object. But the pointer to the current object is this, not &this.
&this, if it was legal, would be a pointer-to-pointer to the current object. But it's illegal, since this (the pointer itself) is a temporary object, and you can't take addresses of those with &.
It would make more sense to ask why we don't do return this;.
The answer is: forming a pointer requires &, but forming a reference doesn't. Compare:
int x = 42;
int *ptr = &x;
int &ref = x;
So, similarly:
int *f1() return {return &x;}
int &f1() return {return x;}
A simple mnemonic you can use is that the * and & operators match the type syntax of the thing you're converting from, not the thing you're converting to:
* converts a foo* to a foo&
& converts a foo& to a foo*
In expressions, there's no meaningful difference between foo and foo&, so I could have said that * converts foo* to foo, but the version above is easier to remember.
C++ inherited its type syntax from C, and C type syntax named types after the expression syntax for using them, not the syntax for creating them. Arrays are written foo x[...] because you use them by accessing an element, and pointers are written foo *x because you use them by dereferencing them. Pointers to arrays are written foo (*x)[...] because you use them by dereferencing them and then accessing an element, while arrays of pointers are written foo *x[...] because you use them by accessing an element and then dereferencing it. People don't like the syntax, but it's consistent.
References were added later, and break the consistency, because there isn't any syntax for using a reference that differs from using the referenced object "directly". As a result, you shouldn't try to make sense of the type syntax for references. It just is.
The reason this is a pointer is also purely historical: this was added to C++ before references were. But since it is a pointer, and you need a reference, you have to use * to get rid of the *.
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.
I'm trying to do this in C++:
class Abc
{
int callFunction1()
};
void function1(Abc** c1) {//do something}
int Abc::callFunction1()
{
function1(&this);
return 0;
}
And I get "expression must be an l-value or function designator" error in visual studio 2015. So I don't understand where I go wrong. To my knowledge, &this should have the type Abc** right?
The function definition isn't mine to change. So I can't just change the parameter type.
The error is clear enough. Since this is not an lvalue, you cannot take its address. If you just want the address of the object, then just pass this, not &this, and change the function declaration to:
void function1(Abc* c1) //To just pass a pointer
However, since you mentioned you cannot change the definition of the function, you can create a temporary variable and pass its address:
auto temp = this;
function1(&temp);
How this works:
Since this is a prvalue and cannot have its address taken, you need something to point to it to turn it into an lvalue, here temp.
Now that temp points to this, taking temp's address will effectively take this's address, albeit indirectly.
Therefore, since you are passing the address of an lvalue to function1, the code compiles and works as expected.
From the C++ Standard (9.2.2.1 The this pointer)
1 In the body of a non-static (9.2.1) member function, the keyword
this is a prvalue expression whose value is the address of the
object for which the function is called.
and (5.3.1 Unary operators)
3 The result of the unary & operator is a pointer to its operand. The
operand shall be an lvalue or a qualified-id....
To make it more clear consider the following code snippet.
If for example you have a declaration
int x = 10;
then you may not write
int **p = &&x;
In the right expression &x is a prvalue and according to the second quote from the Standard you may not apply the unary operator & to the prvalue.
You could write
int *q = &x;
int **p = &q;
because q is lvalue.
The expression this is an rvalue, the same way that the expressions 137 or 'a' are, and so you can't take its address.
If you want to get a pointer to a pointer to this, you'll need to create a new variable of the right type:
auto* ptr = this;
doSomething(&ptr);
it's passed a lot since i used c++ so here the(probally dumb) question:
A basic smart pointer Object should behave like a normal pointer one, so in a typical implementation we add the * and -> operator to the object, something like this:
template <class T> class auto_ptr
{
T* ptr;
public:
explicit auto_ptr(T* p = 0) : ptr(p) {}
~auto_ptr() {delete ptr;}
T& operator*() {return *ptr;}
T* operator->() {return ptr;}
// ...
};
Now, in my knowings, the c++ * operator (dereference) stands for: "get the value pointed in the heap by the value of ptr" (is it right?), and the type of *ptr should be T. So why we would return an address?
T& operator*() {return *ptr;}
Instead of:
T operator*() {return *ptr;}
Second, by having the following snippet:
void foo()
{
auto_ptr<MyClass> p(new MyClass);
p->DoSomething();
}
Now, how can i access ptr->DoSomething() method by just writing p->DoSomething()? Logically i would come writing the wrong code:
p->->DoSomething();
Because p-> returns a T* and then i need another -> operator for access the DoSomething() method.
Thanks for any answer/clarification and sorry for eventually bad English.
In C++, when you evaluate a function, you end up with a value (unless the function's return type is void). The type of a value is always an object type. So when you say f(), that expression is a value of type T. However, there are different categories of value:
T f(); => f() is a prvalue, passed along by copy
T & f(); => f() is an lvalue, the same object that is bound to "return"
T && f(); => f() is an xvalue, the same object that is bound to "return"
So if you want a function to produce an existing value that you don't want to copy, you have to declare the return type of the function as one of the reference types. If the return type is not a reference type, then a copy of the return value will be made, and the caller only ever sees that copy.
The dereference operator returns a reference because then you can do e.g.
*somePointer = someValue;
and the value of what somePointer points to would change to someValue. If you returned by value, the above expression would have a temporary value that is assigned to, and then that temporary value is destructed and the change is lost.
The reason you don't have to write p->->DoSomething is that operator-> recurses until it finds something that isn't a pointer, T*.
p-> finds T* which is a pointer so it goes down another level and finds a MyClass-object, so it stops and does a normal operator. on that.
Note that smart pointers aren't considered pointers in this case.
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.