2

In C++, suppose I have a C++ class called A, and inside it, I define a variable A* ptr;. In the constructor, there is an instruction ptr = this. Now, let's consider the following assignment initialization: A a = A(). My main question is whether a.ptr == &a is always true.

My primary concern is related to this assignment, which first creates a temporary object and then uses either the move constructor (possibly the copy constructor, I'm not entirely sure). I want to understand if the value of the ptr member in the temporary object is always the same as the address of the object it will be assigned to (i.e., whether the address of the temporary object is always the same as the address of a).

I have conducted experiments that seem to confirm this behavior, but I would like to know if this feature is guaranteed by the C++ standard.

Zhang Yuhan
  • 123
  • 4

3 Answers3

3

Assignment happens when you have already constructed two objects and you want to overwrite one with the value of the other. If an object is being constructed right now, that's called initialization, and no assignment takes place to that object.

A a;            // initialization
A b(32);        // initialization
A c = b;        // initialization
b = c;          // assignment
A d = A();      // initialization
A e{42};        // initialization
A *p = nullptr; // initialization (of a pointer)
p = new A();    // assignment of p, initialization of the new A object
*p = d;         // assignment of the A object

In your case the line A a = A(); performs initialization since a is being constructed right now. Since C++17, the initialization is performed in-place whenever the expression is a prvalue, like your A(). No copy is being performed here. The temporary object created by A() is never materialized in memory elsewhere and then copied, it's directly created in the right spot.

Effectively, you can pretend that no temporary object exists in this case. You can equivalently write A a; and obtain the same effect.

chi
  • 111,837
  • 3
  • 133
  • 218
0

Next code shows the temp object, a named object and a copy of a temp object as a function parameter (pass by const reference). Both the temp object as the named object show the same addresses, but the copied temp object shows the new address in ptr, but the old address of the named object.

struct A
{
  A* a;
  A() : a(this) {}
};

void fnc(const A& a)
{
  std::cout << "a.ptr " << a.a << " =?= &a " << &a << "\n";
}

int main()
{
  fnc(A());
  A a;
  fnc(a);
  a = A();
  fnc(a);
}

possible result

a.ptr 000000743053F7D8 =?= &a 000000743053F7D8
a.ptr 000000743053F6F8 =?= &a 000000743053F6F8
a.ptr 000000743053F7F8 =?= &a 000000743053F6F8
stefaanv
  • 14,072
  • 2
  • 31
  • 53
-2

To sum up your question, we have the following:

class A {
    A() {
        ptr = this;
    }
    A* ptr;
}

int main() {
    A a = A();
}

As you stated, the A() statement produces a temporary object. The = operator will call the copy constructor. This does a shallow copy which means pointers are copied but not the pointees. In that case, this produces undefined behavior. It happens that your compiler is smart and directly reuses the temporary object, but it is not guaranteed by the language to my knowledge. When managing pointers, your class should follow the rule of three/five/zero.

In your specific case, I suggest properly specifying the copy constructor to update ptr to the new this address. Although I suppose you are asking this question within a broader program with child objects so it may not be as trivial to implement in your case.

A(const A& other) {
    ptr = this;
}

Following the rule, you should define or delete all related operators (move, constructors, destructor) simlarly.

Edit: Copy elision is guaranteed if using C++17 or above. See comment by 463035818-is-not-a-number . So your original code is safe with that version and up.

deribaucourt
  • 424
  • 8
  • 3
    This is plain wrong. There is no copy assignment here. `A a = A();` is equivalent to `A a;`, only the default constructor will be called, no copy involved (and especailly, no assignment). – Fareanor Jun 12 '23 at 12:15
  • It is equivalent with C++17 and upwards. But my answer is correct with previous C++ standards. See https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization – deribaucourt Jun 12 '23 at 12:16
  • In the link, it's about RVO (return value optimization) which is about functions, not about constructors. Copy elision is not involved with `A a = A();`, it's not a function call, it's a constructor call (well we could see constructors as "special" functions, but the rules are not the same). – Fareanor Jun 12 '23 at 12:18
  • In the link, look for the paragraph "Other common places where copy elision takes place is when an object is constructed from a temporary" – deribaucourt Jun 12 '23 at 12:19
  • Yes, when an object is constructed from a temporary, **which is not the case here**. `A a = A();` does not create a temporary. It's a long version of `A a;` (there is no assignement, the '=' is quite misleading if you prefer) – Fareanor Jun 12 '23 at 12:21
  • The same goes with `A a = A(args);` which is the equivalent of `A a(args);` and only calls a constructor (no assignement). – Fareanor Jun 12 '23 at 12:23
  • If you want assignement to be involved, you need to do it in two steps: `A a; a = A();`. Of course this example is not really useful to assign a default to an already default object, but it's for illustration. – Fareanor Jun 12 '23 at 12:24
  • 1
    Regardless of the difference between C++17 and earlier standards, `A a = A();` has never involved an assignment. Semantically, before C++17, it constructs a temporary `A`, then copies that temporary into `a` - both operations that involve a constructor, not an assignment operator. The difference between earlier standards and C++17 rests in the fact that, in earlier standards, elision of that temporary is permitted but not required whereas in C++17 elision of that temporary is mandatory. – Peter Jun 12 '23 at 12:25
  • Thanks, corrected the answer by replacing copy assignment to copy constructor. Although, I thought that the copy constructor would rather be called with something like `A a(A())`. – deribaucourt Jun 12 '23 at 12:27
  • its either the `=` operator or the copy constructor, they are not the same. "The = operator will call the copy constructor" - no. – 463035818_is_not_an_ai Jun 12 '23 at 13:51
  • "...this produces undefined behavior. " why would there be ub? – 463035818_is_not_an_ai Jun 12 '23 at 13:52
  • With C++14 or less, `ptr` points to the temporary which, if the compiler didn't apply optional copy elision, becomes a dangling pointer. Accessing a dangling pointer is ub. – deribaucourt Jun 12 '23 at 13:54
  • in the code `ptr` is never dereferenced – 463035818_is_not_an_ai Jun 13 '23 at 11:28