Class tagged_union

Synopsis

#include <include/type_safe/tagged_union.hpp>

template <typename... Types>
class tagged_union

Description

A tagged union.

It is much like a plain old C union, but remembers which type it currently stores. It can either store one of the given types or no type at all. \notes Like the C union it does not automatically destroy the currently stored type, and copy operations are deleted. \module variant

Mentioned in

Classes

type_idThe id of a type.

Methods

tagged_union overload
~tagged_union\notes Does not destroy the currently stored type.
destroy\effects Destroys the currently stored type by calling its destructor, and setting the union to the empty state
emplace\effects Creates a new object of given type by perfectly forwarding args.
has_valueReturns: true if there is a type stored, false otherwise.
operator=
typeReturns: The *type_id of the type currently stored, or *invalid_type if there is none.
value overloadReturns: A (const) lvalue/rvalue reference to the currently stored type
value overload\group value

Source

Lines 83-257 in include/type_safe/tagged_union.hpp.

template <typename... Types>
class tagged_union
{
#if defined(__GNUC__) && __GNUC__ < 5
    // does not have is_trivially_copyable
    using trivial = detail::all_of<std::is_trivial<Types>::value...>;
#else
    using trivial = detail::all_of<std::is_trivially_copyable<Types>::value...>;
#endif

    template <class Union>
    friend struct detail::destroy_union;
    template <class Union>
    friend struct detail::copy_union;
    template <class Union>
    friend struct detail::move_union;

public:
    using types = union_types<typename std::decay<Types>::type...>;

    /// The id of a type.
    ///
    /// It is a [ts::strong_typedef]() for `std::size_t`
    /// and provides equality and relational comparison.
    class type_id : public strong_typedef<type_id, std::size_t>,
                    public strong_typedef_op::equality_comparison<type_id>,
                    public strong_typedef_op::relational_comparison<type_id>
    {
    public:
        /// \returns `true` if `T` is a valid type, `false` otherwise.
        template <typename T>
        static constexpr bool is_valid()
        {
            return detail::get_type_index<T, Types...>::value != 0u;
        }

        /// \effects Initializes it to an invalid value.
        /// \notes The invalid value compares less than all valid values.
        constexpr type_id() noexcept : strong_typedef<type_id, std::size_t>(0u) {}

        /// \effects Initializes it to the value of the type `T`.
        /// If `T` is not one of the types of the union types,
        /// it will be the same as the default constructor.
        template <typename T>
        constexpr type_id(union_type<T>) noexcept
        : type_id(detail::get_type_index<T, Types...>::value)
        {}

        /// \returns `true` if the id is valid,
        /// `false` otherwise.
        explicit operator bool() const noexcept
        {
            return *this != type_id();
        }

    private:
        explicit constexpr type_id(std::size_t value) : strong_typedef<type_id, std::size_t>(value)
        {}
    };

    /// A global invalid type id object.
    static constexpr type_id invalid_type = type_id();

    //=== constructors/destructors/assignment ===//
    tagged_union() noexcept = default;

    /// \notes Does not destroy the currently stored type.
    ~tagged_union() noexcept = default;

    tagged_union(const tagged_union&) = delete;
    tagged_union& operator=(const tagged_union&) = delete;

    //=== modifiers ===//
    /// \effects Creates a new object of given type by perfectly forwarding `args`.
    /// \throws Anything thrown by `T`s constructor,
    /// in which case the union will stay empty.
    /// \requires The union must currently be empty.
    /// and `T` must be a valid type and constructible from the arguments.
    template <typename T, typename... Args>
    void emplace(union_type<T>, Args&&... args)
    {
        constexpr auto index = type_id(union_type<T>{});
        // MSVC doesn't like operator!= of the strong typedef here, for some reason...
        static_assert(get(index) != get(invalid_type), "T must not be stored in variant");
        static_assert(std::is_constructible<T, Args&&...>::value,
                      "T not constructible from arguments");

        ::new (get_memory()) T(std::forward<Args>(args)...);
        cur_type_ = index;
    }

    /// \effects Destroys the currently stored type by calling its destructor,
    /// and setting the union to the empty state.
    /// \requires The union must currently store an object of the given type.
    template <typename T>
    void destroy(union_type<T> type) noexcept
    {
        check(type);
        value(type).~T();
        cur_type_ = invalid_type;
    }

    //=== accessors ===//
    /// \returns The [*type_id]() of the type currently stored,
    /// or [*invalid_type]() if there is none.
    const type_id& type() const noexcept
    {
        return cur_type_;
    }

    /// \returns `true` if there is a type stored,
    /// `false` otherwise.
    bool has_value() const noexcept
    {
        return type() != invalid_type;
    }

    /// \returns A (`const`) lvalue/rvalue reference to the currently stored type.
    /// \requires The union must currently store an object of the given type.
    /// \group value
    template <typename T>
    T& value(union_type<T> type) TYPE_SAFE_LVALUE_REF noexcept
    {
        check(type);
        return *static_cast<T*>(get_memory());
    }

    /// \group value
    template <typename T>
    const T& value(union_type<T> type) const TYPE_SAFE_LVALUE_REF noexcept
    {
        check(type);
        return *static_cast<const T*>(get_memory());
    }

#if TYPE_SAFE_USE_REF_QUALIFIERS
    /// \group value
    template <typename T>
        T&& value(union_type<T> type) && noexcept
    {
        check(type);
        return std::move(*static_cast<T*>(get_memory()));
    }

    /// \group value
    template <typename T>
    const T&& value(union_type<T> type) const&& noexcept
    {
        check(type);
        return std::move(*static_cast<const T*>(get_memory()));
    }
#endif

private:
    void* get_memory() noexcept
    {
        return static_cast<void*>(&storage_);
    }

    const void* get_memory() const noexcept
    {
        return static_cast<const void*>(&storage_);
    }

    template <typename T>
    void check(union_type<T> type) const noexcept
    {
        DEBUG_ASSERT(cur_type_ == type, detail::precondition_error_handler{},
                     "different type stored in union");
    }

    using storage_t = detail::aligned_union_t<Types...>;
    storage_t storage_;
    type_id   cur_type_;
};





Add Discussion as Guest

Log in