    // Copyright (C) 2022  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_OVERLOADED_H_
    #define DLIB_OVERLOADED_H_

    #include <utility>
    #include <type_traits>

    namespace dlib
    {
        namespace detail
        {
    #if __cpp_fold_expressions
            template<typename ...Base>
            struct overloaded_helper : Base...
            {
                template<typename... T>
                constexpr overloaded_helper(T&& ... t)
                noexcept((std::is_nothrow_constructible<Base,T&&>::value && ...))
                : Base{std::forward<T>(t)}... {}

                using Base::operator()...;
            };
    #else
            template<typename Base, typename ... BaseRest>
            struct overloaded_helper: Base, overloaded_helper<BaseRest...>
            {
                template<typename T, typename ... TRest>
                constexpr overloaded_helper(T&& t, TRest&& ...trest)
                noexcept(std::is_nothrow_constructible<Base, T&&>::value && std::is_nothrow_constructible<overloaded_helper<BaseRest...>, TRest&&...>::value)
                : Base{std::forward<T>(t)},
                  overloaded_helper<BaseRest...>{std::forward<TRest>(trest)...}
                {}

                using Base::operator();
                using overloaded_helper<BaseRest...>::operator();
            };

            template<typename Base>
            struct overloaded_helper<Base> : Base
            {
                template<typename T>
                constexpr overloaded_helper<Base>(T&& t)
                noexcept(std::is_nothrow_constructible<Base, T&&>::value)
                : Base{std::forward<T>(t)}
                {}

                using Base::operator();
            };
    #endif //__cpp_fold_expressions
        }

        template<typename... T>
        constexpr auto overloaded(T&&... t)
        /*!
            This is a helper function for combining many callable objects (usually lambdas), into
            an overload-able set. This can be used in visitor patterns like
                - dlib::type_safe_union::apply_to_contents()
                - dlib::visit()
                - dlib::for_each_type()
                - dlib::switch_()

            A picture paints a thousand words:

            using tsu = type_safe_union<int,float,std::string>;

            tsu a = std::string("hello there");

            std::string result;

            a.apply_to_contents(overloaded(
                [&result](int) {
                    result = std::string("int");
                },
                [&result](float) {
                    result = std::string("float");
                },
                [&result](const std::string& item) {
                    result = item;
                }
            ));

            assert(result == "hello there");
            result = "";

            result = visit(overloaded(
                [](int) {
                    return std::string("int");
                },
                [](float) {
                    return std::string("float");
                },
                [](const std::string& item) {
                    return item;
                }
            ), a);

            assert(result == "hello there");

            std::vector<int> type_ids;

            for_each_type(a, overloaded(
                [&type_ids](in_place_tag<int>, tsu& me) {
                    type_ids.push_back(me.get_type_id<int>());
                },
                [&type_ids](in_place_tag<float>, tsu& me) {
                    type_ids.push_back(me.get_type_id<float>());
                },
                [&type_ids](in_place_tag<std::string>, tsu& me) {
                    type_ids.push_back(me.get_type_id<std::string>());
                }
            ));

            assert(type_ids == vector<int>({0,1,2}));
        !*/
        noexcept(std::is_nothrow_constructible<detail::overloaded_helper<std::decay_t<T>...>, T&&...>::value)
        {
            return detail::overloaded_helper<std::decay_t<T>...>{std::forward<T>(t)...};
        }
    }

    #endif //DLIB_OVERLOADED_H_