This question has to to do with overloading the assignment operator in c++. Take a look at the following code. It shows the function definition given by my book to overload the assignment operator.
const cAssignmentOprOverload& cAssignmentOprOverload::operator=(
const cAssignmentOprOverload& otherList) {
if (this != &otherList) // avoid self-assignment; Line 1
{
delete[] list; // Line 2
maxSize = otherList.maxSize; // Line 3
length = otherList.length; // Line 4
list = new int[maxSize]; // Line 5
for (int i = 0; i < length; i++) // Line 6
list[i] = otherList.list[i]; // Line 7
}
return *this; // Line 8
}
The biggest issue that is making this hard to understand is the fact that in the definition of the function, it returns *this. Is *this a const object? I don't think it is so why are we allowed to return a non-const object when the return type is supposed to be const?
Inside the body of a non-static member function, the expression this can be used to get a pointer to the object the function has been called on [expr.prim.this]. Since your operator = is not a const member function, this will point to a non-const object (which makes sense since we're assigning a new value to something). Thus, *this will result in a non-const lvalue of type cAssignmentOprOverload. However, a reference to const can be bound to a non-const lvalue [dcl.init.ref]/5.1.1. In general, a less const qualified type can always be implicitly converted to a more const qualified one. Which makes sense: you should be able to use a modifiable object in places where a non-modifiable one is sufficient. Nothing really that can go wrong by treating a modifiable object as non-modifiable. All that happens is that you lose the information that that object was actually modifiable. Just the opposite, treating a non-modifiable object as modifiable, is problematic…
Note that this way of writing an overloaded operator = is not how this is typically done. The canonical form would be
cAssignmentOprOverload& operator=(const cAssignmentOprOverload& otherList)
i.e., returning a reference to non-const…
From implicit_conversion
A prvalue of type pointer to cv-qualified type T can be converted to a prvalue pointer to a more cv-qualified same type T (in other words, constness and volatility can be added).
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);
I am getting an error:
error: invalid initialization of non-const reference of type 'std::string& {aka std::basic_string&}' from an rvalue of type 'std::basic_string'
The code is:
const std::string& hitVarNameConst = (isKO ? "isKO" : "isKI");
for (int i = 0; i < numAssets; i++){
std::string& hitVarName1 = hitVarNameConst + Format::toString(i + 1);
//use hitVarname1 with some other function
}
How do I remove this error? Earlier I was trying the following code, it was still throwing the same error:
for (int i = 0; i < numAssets; i++){
std::string& hitVarName1 = (isKO ? "isKO" : "isKI") + Format::toString(i + 1);
//use hitVarname1 with some other function
}
Neither one of those strings should be a reference. They're objects, pure and simple. Remove the two &s.
You are creating a new string within your loop every time, so creating a reference to it means you are trying to create a reference to an r-value, which cannot be referenced since it has no address (until it is assigned).
Remove the & from the declaration type in your assignment and it should work, or change it to const std::string&, since compilers often allow temporaries to be assigned as references as long as they are constant.
When you have something like hitVarNameConst + Format::toString(i + 1) you get a temporary object out of it. A temporary object in terms of C++ is called an rvalue (rvalue is a historic term, referring to the fact that usually rvalues are found on the right-hand side of the assignment operator (as in a = get_b()). On the left-hand side you have an rvalue.
Non-const references can not be bound to rvalues. You can either have a const reference (which will have a nice effect of extending the lifetime of the temporary) or you can create a new instance of std::string and copy your temporary there. Copy-elision together with move semantics will ensure no real copying will be taking place.
Here is the code which basically implementing the = assignment for a class named CMyString, and the code is right.
CMyString& CMyString::operator =(const CMyString &str) {
if(this == &str)
return *this;
delete []m_pData;
m_pData = NULL;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
The instance is passed by reference, and the first 'if' is checking whether the instance passed in is itself or not. My question is: why does it use &str to compare, doesn't str already contain the address of the instance? Could any one explain how this line works?
Also, I just want to make sure that this contains the address of the object: Is this correct?
isn't str already contains the address of the instance
No. A reference is the object itself. It's not a pointer to the object.
(I. e., in the declaration of the function, &str stands for "reference to str" and not "address of str" - what you're talking about would be right if the function was declared like this:
CMyString& CMyString::operator =(const CMyString *str);
but it isn't.)
Address-of Operator and Reference Operator are different.
The & is used in C++ as a reference declarator in addition to being the address-of operator. The meanings are not identical.
int target;
int &rTarg = target; // rTarg is a reference to an integer.
// The reference is initialized to refer to target.
void f(int*& p); // p is a reference to a pointer
If you take the address of a reference, it returns the address of its target. Using the previous declarations, &rTarg is the same memory address as &target.
str passed by to the assignment operator is passed by reference, so it contains the actual object, not its address. A this is a pointer to the class a method is being called on, so if one wants to compare, whether passed object is the same object itself, he has to get the address of str in order to compare.
Note, that & behaves differently, depending on where it is used. If in statement, it means getting an address to the object it is applied to. On the other hand, if it is used in a declaration, it means, that the declared object is a reference.
Consider the following example:
int i = 42;
int & refToI = i; // A reference to i
refToI = 99;
std::cout << i; // Will print 99
int j = 42;
int * pJ = &j; // A pointer to j
*pJ = 99;
std::cout << j; // Will print 99
this is a pointer to the instance, so yes, it contains the address.
The whole point of verifying, if the passed object is this or not is to avoid unnecessary (or, possibly destructive) assignment to self.
While indeed a variable reference - denoted by the symbol & after the type name - underlying implementation is usually a pointer, the C++ standard seemingly does not specify it.
In its usage anyway, at the syntax level, a reference is used like a non referenced value of the same type, ie. more strictly speaking :
If the type of the variable is T &, then it shall be used as if it were of type T.
If you must write str.someMethod() and not str->someMethod() (without any overloading of the arrow operator), then you must use & to obtain the address of the value. In other words, a reference acts more or less like an alias of a variable, not like a pointer.
For more information about references and pointers, see these questions:
What's the meaning of * and & when applied to variable names?
What are the differences between a pointer variable and a reference variable in C++?
Why 'this' is a pointer and not a reference?