Design a C++ Variant with Safe Object Lifetimes
BalyasnySoftware Engineer · OnsiteLast updated
This problem tests your understanding of manual memory management, template metaprogramming, and the type safety requirements necessary to implement a discriminated union (variant) in C++.
The Task
Implement a simplified, type-safe SafeVariant class in C++ that can hold one of several possible types. Unlike std::variant, your implementation must strictly enforce object lifetimes and provide runtime safety.
Requirements
- Storage: Use
alignasandstd::aligned_storage(orstd::bytearray) to reserve sufficient space for the largest provided type. - Type Tracking: Maintain an internal state to track which index is currently active. If the variant is empty, it should throw a custom exception upon access.
- Lifetime Management: Explicitly call destructors for the held object when the variant is assigned a new type or destroyed.
- Interface:
template class SafeVarianttemplate void set(T&& val): Constructs an object of typeTin the storage.template T& get(): Returns a reference to the held object if the type matches; throw an exception otherwise.~SafeVariant(): Ensures the active object is properly destroyed.
Constraints
- You may assume
Argsare movable/copyable types. - The size of the
SafeVariantshould bemax(sizeof(Args...)) + sizeof(size_t)(for the index).
Example
SafeVariant v;
v.set(42);
int val = v.get(); // Returns 42
v.set("hello"); // Properly destroys 42, constructs string
Edge Cases
- Attempting to
getthe wrong type. - Exception safety during the construction of a new type (ensure the old type is preserved if the new one fails to construct).
Hints
Hint 1
Use a variadic template pack to determine the maximum size and alignment of the storage buffer using `std::max` and `std::max_align_t`.
Hint 2
Use `std::index_sequence` and a helper function with a switch statement or recursive template to route calls to the correct destructor based on the active index.
Hint 3
To handle exception safety, construct the new object in a temporary buffer before destroying the existing object, ensuring the variant remains in a valid state if construction throws.
Follow-up questions
- How would you extend this design to support types that are not default-constructible?
- What changes are required to make the `get` method `constexpr` compatible?
- How does your implementation handle memory alignment issues if the user provides a type with an alignment requirement larger than `std::max_align_t`?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.