The Vee - 3 months ago 21

C++ Question

Consider a hypothetical scenario where two classes can be default-constructed or constructed from each other but either way is considered to be expensive (intentionally contrived example follows):

`struct PrivateKey;`

struct PublicKey {

PublicKey(); // generate a random public key (1 minute)

PublicKey(const PrivateKey& b); // find a public key corresponding to a private key (1 year)

...members...

};

struct PrivateKey {

PrivateKey(); // generate a random private key (1 minute)

PrivateKey(const PublicKey& a); // find a private key corresponding to a public key (1 year)

...members...

};

(This could of course be condensed to one class but the validity of the question is unaffected. Let's say for the sake of coherence there's no symmetry between one and the other.)

Now there's a structure which holds instances of both and needs this cross-initialization. However, we may need both directions, so initializer lists can't really cut it (they aren't run in the order listed but in the order the members are defined, and no order can be fixed here):

`struct X {`

PublicKey a;

PrivateKey b;

X(int): a(), b(a) { }

X(float): b(), a(b) { } // UB: a(b) happens before b is initialized

};

Of course I could try:

`struct X {`

PublicKey a;

PrivateKey b;

X(int): a(), b(a) { }

X(float): a(), b() { a = PublicKey(b); }

};

but this has multiple issues, of which

`PublicKey`

`X`

`PublicKey::PublicKey()`

`X`

`X::X(float)`

Answer

The construction order issue can be avoided by using pointers to the contained classes, instead of embedding the contained classes directly, and constructing the contained objects yourself inside the body of the constructor.

```
struct X {
std::unique_ptr<PublicKey> a;
std::unique_ptr<PrivateKey> b;
X(int) {
a = std::make_unique<PublicKey>();
b = std::make_unique<PrivateKey>(*a);
}
X(float) {
b = std::make_unique<PrivateKey>();
a = std::make_unique<PublicKey>(*b);
}
};
```