Reputation: 2748
Suppose I have the following code (it may be oversimplified but I think it will be enought) :
struct BigObject {
A a;
B b;
};
struct A { ... };
struct B {
A const* a_ptr;
MyOtherClass someMethodUsingA() { ... }
}
class BigObjectBuilder {
BigObject build() {
BigObject o;
o.a = buildA();
o.b.a_ptr = &(o.a);
return o;
}
}
The problem is that &(o.a) may point to anything later on because o's address may have changed when it is returned (either the move or the copy constructor is called). Now I found that most of the time &(o.a) is the same so that calling *(o.b.a_ptr) won't result in a segfault. (I think this is due to RVO because then o is not moved). Anyway this code is not correct as long as I'm not sure that the address of o has not changed.
An obvious solution would be to ask for a dynamic allocation of my BigObject:
auto o_ptr = make_unique<BigObject>();
This solution is not too bad (no leaks, and solves the previous problem.), however I still found it inelegant: I don't NEED a dynamic allocation, I only need to have a fixed address for my BigObject. This would be the Java way of doing things I think. Another solution would be to use a copy in B, but then all changes in o.a won't affect o.b.a, and I don't want that.
A third solution would be to have no copy, ptr or references of A in B, and passing A in argument of the method B::someMethodUsingA(). But again this could be tedious to find what argument to pass when calling this function. Sometimes it just feels more natural for the class B to have a pointer to class A.
Now I found this problem happening again and again in my code: I want to build a complex object, with subobjects referencing each other. The fundamental problem is that the object is not created "in place", it may be moved later on during the build process (but not after everything has been built).
Is their any known pattern that can apply to this ? I suppose this is quite a common problem, and if not it must be because I am not building my system the right way...
Is there a way to ensure some kind of RVO ? I mean, ensured by the standard, not by the compiler. Something like a compiler warning saying "BigObject" can't be moved to a different address"
I also thought of deleting the move and copy ctor and assignment operator. But then I am unable to return an object by value, even if in practice nothing is moved...
Any share of what is working for you is welcome !
Upvotes: 1
Views: 188
Reputation: 438
I can not understand why you need a struct object and its pointer in struct BigObject
.
You can design struct like this:
struct A { ... };
struct BigObject {
A a;
MyOtherClass someMethodUsingA() { /* Here you can use the variable a. */ }
};
If you still want to code by your way, you can add copy-construct and assign-construct function for struct BigObject
, like this:
struct BigObject
{
A a;
B b;
// copy-construct function
BigObject(const BigObject& old)
{
a = old.a;
b = old.b;
b.a_ptr = &a; // pay attetion
}
// assign construct function
BigObject& operator=(const BigObject& old)
{
if (this == &old) // avoid self-assignment
{
return *this;
}
a = old.a;
b = old.b;
b.a_ptr = &a;
return *this;
}
};
Upvotes: 1
Reputation: 21763
Add an A reference parameter to B's functions, and when BigObject calls the functions, it can pass its A. A and B should be private. Add wrapper functions to BigObject if necessary, that redirect to calls to A and B, passing the A parameter as required.
Don't write code that requires RVO to work.
Upvotes: 0