# Boost.Compat

C++11 Implementations of Standard Components Added in Later C++ Standards

## Overview

Boost.Compat is a repository of C++11 implementations of standard components added in later C++ standards. Its target audience is Boost library authors whose libraries support a lower C++ standard, but wish to utilize a component added in a subsequent one.

The criteria for inclusion in Boost.Compat are as follows:

- The implementation should be relatively simple and header-only.

- The component must implement the standard functionality exactly, without deviations or extensions. This allows (but does not require) the implementation to be a simple `using` declaration in case the standard component is available.

- The component must not depend on any Boost libraries except Boost.Config, Boost.Assert, or Boost.ThrowException.

- The component must not be a vocabulary type, visible in the library APIs. The user should never see a `boost::compat` type; the use of Compat types should be confined to library implementations.

## Revision History

### Changes in 1.87.0

- Added `to_array.hpp` (contributed by Ruben Perez Hidalgo.)

### Changes in 1.86.0

- Added `bind_front.hpp`, `bind_back.hpp`, `invoke.hpp`, `mem_fn.hpp`, `integer_sequence.hpp` and `type_traits.hpp`.

- Added `function_ref.hpp`.

### Changes in 1.83.0

- Added `latch.hpp`, an implementation of `std::latch` (contributed by Christian Mazakas.)

- Added `shared_lock.hpp`, a (partial) implementation of `std::shared_lock` (contributed by Christian Mazakas.)

## <boost/compat/bind_back.hpp>

### Description

The header `<boost/compat/bind_back.hpp>` implements the C++20 function `std::bind_back`.

`bind_back` is a limited variant of `std::bind`. It only supports binding the last several parameters of a function object to specific argument values.

### Example

    struct X
    {
        void f(int a, int b) const noexcept;
    };

    int main()
    {
        auto fn = boost::compat::bind_back(&X::f, 1, 2);

        X x;
        fn(x); // calls x.f(1, 2)
    }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template<class F, class... A> auto bind_back(F&& f, A&&... a);

    } // namespace compat
    } // namespace boost

### bind_back

    template<class F, class... A> auto bind_back( F&& f, A&&... a );

Returns: | A function object `fn` such that `fn(b…​)` is equivalent to `invoke(f, b…​, a…​)`.

## <boost/compat/bind_front.hpp>

### Description

The header `<boost/compat/bind_front.hpp>` implements the C++20 function `std::bind_front`.

`bind_front` is a limited variant of `std::bind`. It only supports binding the first several parameters of a function object to specific argument values.

### Example

    struct X
    {
        void f(int a, int b) const noexcept;
    };

    int main()
    {
        X x;
        auto fn = boost::compat::bind_front(&X::f, &x);

        fn(1, 2); // calls x.f(1, 2)
    }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template<class F, class... A> auto bind_front(F&& f, A&&... a);

    } // namespace compat
    } // namespace boost

### bind_front

    template<class F, class... A> auto bind_front(F&& f, A&&... a);

Returns: | A function object `fn` such that `fn(b…​)` is equivalent to `invoke(f, a…​, b…​)`.

## <boost/compat/function_ref.hpp>

### Description

The header `<boost/compat/function_ref.hpp>` implements the C++26 class `std::function_ref`.

`function_ref` is a lightweight polymorphic function wrapper that only stores a pointer to the supplied Callable object and a pointer to an unspecified function, meaning it does not participate in ownership of the Callable and does not allocate. All specializations of `function_ref` satisfy TriviallyCopyable.

`function_ref` supports every combination of `const` and `noexcept` and is useful for writing higher-order functions as it can avoid a template parameter or an allocation (as `std::function` is known for).

### Example

    int add(int x, int y) noexcept { return x * 10 + y; }

    auto add2 = [](int x, int y) { return x * 100 + y; };

    std::vector<boost::compat::function_ref<int(int, int)>> fns;
    fns.push_back({add});
    fns.push_back({add2});

    for (auto fn : fns) {
      std::cout << fn(1, 2) << std::endl;
    }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template <class... S>
    struct function_ref;

    // cv is either `const` or empty
    // noex is either `true` or `false`
    template<class R, class... ArgTypes>
    class function_ref<R(ArgTypes...) cv noexcept(noex)> {
    public:
      template<class F> function_ref(F*) noexcept;
      template<class F> function_ref(F&&) noexcept;
      template<auto f> function_ref(nontype_t<f>) noexcept;
      template<auto f, class U> function_ref(nontype_t<f>, U&&) noexcept;
      template<auto f, class T> function_ref(nontype_t<f>, cv T*) noexcept;

      function_ref(const function_ref&) noexcept = default;
      function_ref& operator=(const function_ref&) noexcept = default;
      template<class T> function_ref& operator=(T) = delete;

      R operator()(ArgTypes...) const noexcept(noex);
    };

    } // namespace compat
    } // namespace boost

### Constructors

#### Function Pointer Constructor

    template<class F> function_ref(F* fn) noexcept;

Preconditions | `fn` != `nullptr`.
Effects | Constructs a `function_ref` which uses the supplied function pointer as its Callable. Calling the `function_ref` is expression-equivalent to `invoke_r<R>(f, call-args…​)`.

#### Object Constructor

    template<class F> function_ref(F&& fn) noexcept;

Effects: | Constructs a `function_ref` that stores the address of the supplied Callable object `fn`. This overload only participates in resolution when `fn` is not a pointer-to-member or pointer-to-member-function. Calling the `function_ref` is expression-equivalent to: + `invoke_r<R>(static_cast<cv T&>(f), call-args…​)`.

#### Pointer to Member Function Constructor

    template<auto f> function_ref(nontype_t<f>) noexcept;

Effects: | Constructs a `function_ref` using the supplied pointer to member function. This overload only participates in resolution when `f` is a pointer to member or pointer to member function. Calling the `function_ref` is express-equivalent to: `invoke_r<R>(f, class-args)`.

Example:

    struct point { int x = 1, y = 2; };

    point p;
    compat::function_ref<int(point const&)> f(compat::nontype_t<&point::x>{});

    BOOST_TEST_EQ(f(p), 1);

#### Bound Object Constructor

    template<auto f, class U> function_ref(nontype_t<f>, U&&) noexcept;

Effects: | Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a reference to an object to bind it to. This overload only participates in resolution if `is_rvalue_reference_v<U&&>` is false.

Example:

    struct point { int x = 1, y = 2; };

    point p;
    compat::function_ref<int()> f(compat::nontype_t<&point::x>{}, p);

    BOOST_TEST_EQ(f(), 1);

#### Bound Pointer Constructor

    template<auto f, class T> function_ref(nontype_t<f>, cv T*) noexcept;

Effects: | Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a pointer to an object.

Example:

    struct point { int x = 1, y = 2; };

    point p;
    compat::function_ref<int()> f(compat::nontype_t<&point::x>{}, &p);

    BOOST_TEST_EQ(f(), 1);

#### Copy Constructor

    function_ref(const function_ref&) noexcept = default;

Effects: | `function_ref` is a TriviallyCopyable type.

### Member Functions

#### call operator

    R operator()(ArgTypes...) const noexcept(noex);

Effects: | Invokes the underlying Callable object by forwarding the supplied arguments.

### Assignment

#### Copy Assignment

    function_ref& operator=(const function_ref&) noexcept = default;
    template<class T> function_ref& operator=(T) = delete;

Effects:

`operator=(T)` participates in overload resolution if:

- `T` is not the same as `function_ref`

- `is_pointer_v<T>` is `false`.

## <boost/compat/integer_sequence.hpp>

### Description

The header `<boost/compat/integer_sequence.hpp>` implements the C++14 utilities `std::integer_sequence`, `std::index_sequence`, `std::make_integer_sequence`, `std::make_index_sequence`, and `std::index_sequence_for`.

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template<class T, T... I> struct integer_sequence;

    template<class T, T N> using make_integer_sequence = /*...*/;

    template<std::size_t... I> using index_sequence = /*...*/;

    template<std::size_t N> using make_index_sequence = /*...*/;

    template<class... T> using index_sequence_for = /*...*/;

    } // namespace compat
    } // namespace boost

### integer_sequence

    template<class T, T... I> struct integer_sequence {};

### make_integer_sequence

    template<class T, T N> using make_integer_sequence = /*...*/;

`make_integer_sequence<T, N>` is an alias for `integer_sequence<T, 0, 1, 2, …​, N-1>`.

### index_sequence

    template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>;

### make_index_sequence

    template<std::size_t N> using make_index_sequence = make_integer_sequence<std::size_t, N>;

### index_sequence_for

    template<class... T> using index_sequence_for = make_integer_sequence<std::size_t, sizeof...(T)>;

## <boost/compat/invoke.hpp>

### Description

The header `<boost/compat/invoke.hpp>` implements the C++17 function `std::invoke`, the C++23 function `std::invoke_r`, and the associated utilities `invoke_result_t`, `is_invocable`, `is_invocable_r`, `is_nothrow_invocable`, and `is_nothrow_invocable_r`.

`invoke(f, a…​)` generally returns `f(a…​)`, but when `f` is a pointer to member, it invokes it as if by returning `mem_fn(f)(a…​)`. This allows functions, function objects, and pointers to members to be treated uniformly by components such as `bind_front`.

`invoke_r<R>(f, a…​)` returns `invoke(f, a…​)`, converted to `R`.

### Example

    struct X
    {
        void f(int a, int b) const noexcept;
    };

    int main()
    {
        X x;
        boost::compat::invoke(&X::f, x, 1, 2); // calls x.f(1, 2)
    }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template<class F, class... A> auto invoke(F&& f, A&&... a);

    template<class F, class... A> using invoke_result_t = /*...*/;

    template<class F, class... A> struct is_invocable;

    template<class F, class... A> struct is_nothrow_invocable;

    template<class R, class F, class... A> R invoke_r(F&& f, A&&... a);

    template<class R, class F, class... A> struct is_invocable_r;

    template<class R, class F, class... A> struct is_nothrow_invocable_r;

    } // namespace compat
    } // namespace boost

### invoke

    template<class F, class... A> auto invoke(F&& f, A&&... a) noexcept(/*...*/) -> /*...*/;

Returns:

- `std::forward<F>(f)(std::forward<A>(a)…​)`, when `f` is not a pointer to member;

- `mem_fn(f)(std::forward<A>(a)…​)`, otherwise.

Constraints: | the return expression must be valid.
Remarks: | The return type is `decltype(r)`, and the `noexcept` clause is `noexcept(noexcept(r))`, where `r` is the return expression.

### invoke_result_t

    template<class F, class... A> using invoke_result_t =
      decltype( invoke(std::declval<F>(), std::declval<A>()...) );

### is_invocable

    template<class F, class... A> struct is_invocable: public /*...*/;

The base class of `is_invocable<F, A…​>` is `std::true_type` when `invoke(std::declval<F>(), std::declval<A>()…​)` is a valid expression, `std::false_type` otherwise.

### is_nothrow_invocable

    template<class F, class... A> struct is_nothrow_invocable: public /*...*/;

The base class of `is_nothrow_invocable<F, A…​>` is `std::false_type` when `is_invocable<F, A…​>::value` is `false`, `std::integral_constant<bool, noexcept(invoke(std::declval<F>(), std::declval<A>()…​))>` otherwise.

### invoke_r

    template<class R, class F, class... A> R invoke_r(F&& f, A&&... a) noexcept(/*...*/);

Returns:

- `static_cast<R>(invoke(std::forward<F>(f), std::forward<A>(a)…​))`, when `R` is (possibly cv-qualified) `void`;

- `invoke(std::forward<F>(f), std::forward<A>(a)…​)`, implicitly converted to `R`, otherwise.

Constraints: | `is_invocable<F, A…​>::value` must be `true` and, if `R` is not cv `void`, `std::is_convertible<invoke_result_t<F, A…​>, R>::value` must be `true`.
Remarks: | The `noexcept` clause is `noexcept(noexcept(static_cast<R>(invoke(std::forward<F>(f), std::forward<A>(a)…​))))`.

### is_invocable_r

    template<class R, class F, class... A> struct is_invocable: public /*...*/;

The base class of `is_invocable_r<R, F, A…​>` is `std::true_type` when `invoke_r<R>(std::declval<F>(), std::declval<A>()…​)` is a valid expression, `std::false_type` otherwise.

### is_nothrow_invocable_r

    template<class R, class F, class... A> struct is_nothrow_invocable: public /*...*/;

The base class of `is_nothrow_invocable<R, F, A…​>` is `std::false_type` when `is_invocable_r<R, F, A…​>::value` is `false`, `std::integral_constant<bool, noexcept(invoke_r<R>(std::declval<F>(), std::declval<A>()…​))>` otherwise.

## <boost/compat/latch.hpp>

### Description

The header `<boost/compat/latch.hpp>` implements, in a portable way, the C++20 `<latch>` header.

`latch` is a single-use barrier which counts downwards, enabling synchronization between a set of threads. The counter can be manually decremented by any value, but decrementing below zero causes undefined behavior. The maximum number of waiters is specified to be `boost::compat::latch::max()`.

### Example

    std::ptrdiff_t const num_threads = 16;
    boost::compat::latch l(num_threads);

    std::vector<std::thread> threads;
    for (int i = 0; i < num_threads; ++i) {
      threads.emplace_back([&l] {
        // do some preliminary work here...

        // block until all threads have reached this statement
        l.arrive_and_wait();

        // continue with further work...
      });
    }

    for (auto& t: threads) { t.join(); }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    class latch {
      explicit latch(std::ptrdiff_t expected);

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

      ~latch() = default;

      void count_down(std::ptrdiff_t n = 1);
      bool try_wait() const noexcept;
      void wait() const;
      void arrive_and_wait(std::ptrdiff_t n = 1);

      static constexpr std::ptrdiff_t max() noexcept;
    };

    } // namespace compat
    } // namespace boost

### Constructors

#### Counter Constructor

    explicit latch(std::ptrdiff_t expected);

Preconditions: | `expected >= 0 && expected <= boost::compat::latch::max()`.
Effects: | Constructs a latch with an internal counter value of `expected`.

#### Copy Constructor

    latch(latch const &) = delete;

`latch` is not copyable or movable.

### Member Functions

#### count_down

    void count_down(std::ptrdiff_t n = 1);

Preconditions: | `n` must not be larger than the current internal counter’s value.
Effects: | Decrements the internal counter by `n`.

#### try_wait

    bool try_wait() const noexcept;

Returns a boolean indicating whether or not the latch’s internal counter has reached 0 (`true`) or not (`false`).

#### wait

    void wait() const;

Blocks the current thread until the internal counter has reached 0.

#### arrive_and_wait

    void arrive_and_wait(std::ptrdiff_t n = 1);

Preconditions: | `n` must not be larger than the current internal counter’s value.
Effects: | Decrements the internal counter by `n` and if the counter non-zero, blocks the current thread.

#### max

    static constexpr std::ptrdiff_t max() noexcept;

Returns an implementation-defined number representing the maximum amount of waiters. Currently PTRDIFF_MAX.

## <boost/compat/mem_fn.hpp>

### Description

The header `<boost/compat/mem_fn.hpp>` implements the C++11 function `std::mem_fn`.

`mem_fn(pm)`, where `pm` is a pointer to member, returns a function object that can be used to invoke the member function, or obtain a reference to the data member, using a function call syntax.

Even though `std::mem_fn` is C++11, later standards place stricter requirements on the returned function object (it needs to be SFINAE friendly and propagate `noexcept` correctly.) `boost::compat::mem_fn` implements these stricter requirements.

### Example

    struct X
    {
        void f(int a, int b) const noexcept;
    };

    int main()
    {
        auto fn = boost::compat::mem_fn(&X::f);

        X x;
        fn(x, 1, 2); // calls x.f(1, 2)
    }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template<class M, class T> auto mem_fn(M T::* pm) noexcept;

    } // namespace compat
    } // namespace boost

### mem_fn

    template<class M, class T> auto mem_fn(M T::* pm) noexcept;

Returns:

A function object `fn` such that:

- `fn(x, a…​)` is equivalent to `(x.*pm)(a…​)`, when `M` is a function type and the type of `X` is `T` or derived from `T`;

- `fn(x, a…​)` is equivalent to `((*x).*pm)(a…​)`, when `M` is a function type and the type of `X` is not `T` or derived from `T`;

- `fn(x)` is equivalent to `x.*pm`, when `M` is an object type and the type of `X` is `T` or derived from `T`;

- `fn(x)` is equivalent to `(*x).*pm`, when `M` is an object type and the type of `X` is not `T` or derived from `T`.

## <boost/compat/shared_lock.hpp>

### Description

The header `<boost/compat/shared_lock.hpp>` implements, in a portable way, the C++14 `std::shared_lock` class template.

The class `shared_lock` is a RAII wrapper that manages locking and unlocking the provided Mutex, provided that it implements SharedLockable.

This is the shared analog of unique_lock and calls `lock_shared()` instead of `lock()`.

### Example

    #include <boost/compat/shared_lock.hpp>

    shared_mutex m;

    // acquire the lock by calling `m.lock_shared()`
    // `m.unlock_shared()` is called automatically for us by `guard` now
    boost::compat::shared_lock<shared_mutex> guard(m);
    assert(guard.owns_lock());

### Synopsis

    namespace boost {
    namespace compat {

    template <class Mutex>
    class shared_lock;

    template <class Mutex>
    void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;

    template <class Mutex>
    class shared_lock {
      using mutex_type = Mutex;

      shared_lock() noexcept = default;
      explicit shared_lock( mutex_type& m );
      shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;
      shared_lock( mutex_type& m, std::try_lock_t );
      shared_lock( mutex_type& m, std::adopt_lock_t );

      ~shared_lock();

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

      shared_lock( shared_lock&& u ) noexcept;
      shared_lock& operator=( shared_lock&& u ) noexcept;

      void lock();
      bool try_lock();
      void unlock();

      void swap( shared_lock& u ) noexcept;
      mutex_type* release() noexcept;

      mutex_type* mutex() const noexcept;

      bool owns_lock() const noexcept;
      explicit operator bool() const noexcept;
    };

    } // namespace compat
    } // namespace boost

### Constructors

#### Default Constructor

    shared_lock() noexcept = default;

Postconditions: | `mutex() == nullptr` and `owns_lock() == false`.

#### Locking Constructor

    explicit shared_lock( mutex_type& m );

Effects: | Calls `m.lock_shared()`.
Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == true`.

#### Deferred Constructor

    shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;

Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == false`.

#### Try-to-Lock Constructor

    shared_lock( mutex_type& m, std::try_lock_t );

Effects: | Calls `m.try_lock_shared()`.
Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == res` where `res` is the result of the `m.try_lock_shared()` call.

#### Adopting Constructor

    shared_lock( mutex_type& m, std::adopt_lock_t );

Preconditions: | `m` must be held by a previous call to `m.lock_shared()` or a successful call to `m.try_lock_shared()`.
Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == true`.

#### Copy Constructor

    shared_lock( const shared_lock& ) = delete;

`shared_lock` is not copyable.

#### Move Constructor

    shared_lock( shared_lock&& u ) noexcept;

Postconditions: | `mutex() == s.mutex()` and `owns_lock() == s.owns_lock()` where `s` is the state of `u` before move. `u.mutex() == nullptr` and `u.owns_lock() == false` after move.

### Assignment

#### Copy Assignment

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

`shared_lock` is not copyable.

#### Move Assignment

    shared_lock& operator=( shared_lock&& u ) noexcept;

Effects: | If `owns_lock() == true`, calls `unlock()`.
Postconditions: | `mutex() == s.mutex()` and `owns_lock() == s.owns_lock()` where `s` is the state of `u` before move. `u.mutex() == nullptr` and `u.owns_lock() == false` after move.

### Destructor

    ~shared_lock();

Effects: | If `owns_lock() == true`, calls `unlock()`.

### Member Functions

#### Locking

##### lock

    void lock();

Effects: | Calls `mutex()->lock_shared()`.
Postconditions: | `owns_lock() == true`.
Throws: | Any exception caused by `mutex()->lock_shared()`. `std::system_error` when `mutex() == nullptr` (with `std::errc::operation_not_permitted`) or `owns_lock() == true` (with `std::errc::resource_deadlock_would_occur`).

##### try_lock

    bool try_lock();

Effects: | Calls `mutex()->try_lock_shared()`.
Postconditions: | `owns_lock() == res` where `res = mutex()->try_lock_shared()`.
Throws: | Any exception caused by `mutex()->try_lock_shared()`. `std::system_error` when `mutex() == nullptr` (with `std::errc::operation_not_permitted`) or `owns_lock() == true` (with `std::errc::resource_deadlock_would_occur`).

##### unlock

    void unlock();

Effects: | Calls `mutex()->unlock_shared()`.
Postconditions: | `owns_lock() == false`.
Throws: | `std::system_error` (with `std::errc::operation_not_permitted`) if `owns_lock() == false`.

#### Modifiers

##### swap

    void swap( shared_lock& u ) noexcept;

Effects: | Swaps the data members of `*this` and `u`.

##### release

    mutex_type* release() noexcept;

Postconditions: | `mutex() == nullptr` and `owns_lock() == false`.
Returns: | The previous value of `mutex()`.

##### Free Function swap

    template <class Mutex>
    void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;

Effects: | Swaps the data members of `x` and `y` via `x.swap(y)`.

#### Observers

##### mutex

    mutex_type* mutex() const noexcept;

Returns: | The value of the internal pointer, either `nullptr` or the address of the mutex.

##### owns_lock

    bool owns_lock() const noexcept;

Returns: | A boolean indicating whether or not the mutex is locked by the current `shared_lock` instance.

##### boolean conversion

    explicit operator bool() const noexcept;

Returns: | A boolean indicating whether or not the mutex is locked by the current `shared_lock` instance.

## <boost/compat/to_array.hpp>

### Description

The header `<boost/compat/to_array.hpp>` implements, in a portable way, the C++20 `std::to_array` function, present in the `<array>` header.

`to_array` creates a `std::array` from a single-dimensional C array, performing an element-wise copy or move.

### Example

    int input [] = {1, 2, 3};
    std::array<int, 3> output = boost::compat::to_array(input);
    assert((
        output == std::array<int, 3>{{1, 2, 3}}
    ));

### Synopsis

    namespace boost
    {
    namespace compat
    {

    template <class T, std::size_t N>
    constexpr std::array<remove_cv_t<T>, N> to_array(T (&a)[N]);

    template <class T, std::size_t N>
    constexpr std::array<remove_cv_t<T>, N> to_array(T (&&a)[N]);

    } // namespace compat
    } // namespace boost

### to_array

    template <class T, std::size_t N>
    constexpr std::array<remove_cv_t<T>, N> to_array(T (&&a)[N]);

Effects: | Creates an array of `N` elements by copying elements in `a`. For every `i` in `0, …​, N-1`, copy-initializes the i-th element in the output array from `a[i]`.
Type requirements: | `std::is_constructible_v<remove_cv_t<T>, T&> && !std::is_array_v<T>`. Otherwise, the overload is ill-formed.

    template <class T, std::size_t N>
    constexpr std::array<remove_cvref_t<T>, N> to_array(T (&a)[N]);

Effects: | Creates an array of `N` elements by moving elements in `a`. For every `i` in `0, …​, N-1`, move-initializes the i-th element in the output array from `std::move(a[i])`.
Type requirements: | `std::is_constructible_v<remove_cv_t<T>, T&&> && !std::is_array_v<T>`. Otherwise, the overload is ill-formed.

## <boost/compat/type_traits.hpp>

### Description

The header `<boost/compat/type_traits.hpp>` implements some of the post-C++11 additions to the standard header `<type_traits>`.

### Synopsis

    #include <type_traits>

    namespace boost
    {
    namespace compat
    {

    template<class T> using remove_const_t = typename std::remove_const<T>::type;

    template<class T> using remove_cv_t = typename std::remove_cv<T>::type;

    template<class T> using remove_reference_t = typename std::remove_reference<T>::type;

    template<class T> using remove_cvref_t = remove_cv_t<remove_reference_t<T>>;

    template<class T> using decay_t = typename std::decay<T>::type;

    template<bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;

    template<bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;

    template<class... T> using void_t = void;

    } // namespace compat
    } // namespace boost

## Copyright and License

This documentation is copyright 2023, 2024 Peter Dimov and contributors and is distributed under the Boost Software License, Version 1.0.