General utilities library

Constness propagation

Header <experimental/propagate_const> synopsis

namespace std {
  namespace experimental::inline fundamentals_v3 {

    
    template <class T> class propagate_const;

    
    template <class T>
      constexpr bool operator==(const propagate_const<T>& pt, nullptr_t);
    template <class T>
      constexpr bool operator==(nullptr_t, const propagate_const<T>& pu);

    template <class T>
      constexpr bool operator!=(const propagate_const<T>& pt, nullptr_t);
    template <class T>
      constexpr bool operator!=(nullptr_t, const propagate_const<T>& pu);

    template <class T, class U>
      constexpr bool operator==(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator!=(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator<(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator>(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator<=(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator>=(const propagate_const<T>& pt,
                                const propagate_const<U>& pu);

    template <class T, class U>
      constexpr bool operator==(const propagate_const<T>& pt, const U& u);
    template <class T, class U>
      constexpr bool operator!=(const propagate_const<T>& pt, const U& u);
    template <class T, class U>
      constexpr bool operator<(const propagate_const<T>& pt, const U& u);
    template <class T, class U>
      constexpr bool operator>(const propagate_const<T>& pt, const U& u);
    template <class T, class U>
      constexpr bool operator<=(const propagate_const<T>& pt, const U& u);
    template <class T, class U>
      constexpr bool operator>=(const propagate_const<T>& pt, const U& u);

    template <class T, class U>
      constexpr bool operator==(const T& t, const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator!=(const T& t, const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator<(const T& t, const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator>(const T& t, const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator<=(const T& t, const propagate_const<U>& pu);
    template <class T, class U>
      constexpr bool operator>=(const T& t, const propagate_const<U>& pu);

    
    template <class T>
      constexpr void swap(propagate_const<T>& pt,
                          propagate_const<T>& pt2) noexcept(see below);

    
    template <class T>
      constexpr const T& get_underlying(const propagate_const<T>& pt) noexcept;
    template <class T>
      constexpr T& get_underlying(propagate_const<T>& pt) noexcept;

  } // namespace experimental::inline fundamentals_v3

  
  template <class T> struct hash;
  template <class T>
    struct hash<experimental::fundamentals_v3::propagate_const<T>>;

  
  template <class T> struct equal_to;
  template <class T>
    struct equal_to<experimental::fundamentals_v3::propagate_const<T>>;
  template <class T> struct not_equal_to;
  template <class T>
    struct not_equal_to<experimental::fundamentals_v3::propagate_const<T>>;
  template <class T> struct less;
  template <class T>
    struct less<experimental::fundamentals_v3::propagate_const<T>>;
  template <class T> struct greater;
  template <class T>
    struct greater<experimental::fundamentals_v3::propagate_const<T>>;
  template <class T> struct less_equal;
  template <class T>
    struct less_equal<experimental::fundamentals_v3::propagate_const<T>>;
  template <class T> struct greater_equal;
  template <class T>
    struct greater_equal<experimental::fundamentals_v3::propagate_const<T>>;

} // namespace std

Class template propagate_const

Overview

namespace std::experimental::inline fundamentals_v3 {

  template <class T> class propagate_const {
  public:
    using element_type = remove_reference_t<decltype(*declval<T&>())>;

    
    constexpr propagate_const() = default;
    propagate_const(const propagate_const& p) = delete;
    constexpr propagate_const(propagate_const&& p) = default;
    template <class U>
      explicit(!is_convertible_v<U, T>)
      constexpr propagate_const(propagate_const<U>&& pu);
    template <class U>
      explicit(!is_convertible_v<U, T>)
      constexpr propagate_const(U&& u);

    
    propagate_const& operator=(const propagate_const& p) = delete;
    constexpr propagate_const& operator=(propagate_const&& p) = default;
    template <class U>
      constexpr propagate_const& operator=(propagate_const<U>&& pu);
    template <class U>
      constexpr propagate_const& operator=(U&& u);

    
    explicit constexpr operator bool() const;
    constexpr const element_type* operator->() const;
    constexpr operator const element_type*() const; // Not always defined
    constexpr const element_type& operator*() const;
    constexpr const element_type* get() const;

    
    constexpr element_type* operator->();
    constexpr operator element_type*(); // Not always defined
    constexpr element_type& operator*();
    constexpr element_type* get();

    
    constexpr void swap(propagate_const& pt) noexcept(is_nothrow_swappable<T>);

  private:
    T t_; //exposition only
  };

} // namespace std::experimental::inline fundamentals_v3

propagate_const is a wrapper around a pointer-like object type T which treats the wrapped pointer as a pointer to const when the wrapper is accessed through a const access path.

General requirements on T

T shall be a cv-unqualified pointer-to-object type or a cv-unqualified class type for which decltype(*declval<T&>()) is an lvalue reference to object type; otherwise the program is ill-formed.

propagate_const<const int*> is well-formed but propagate_const<int* const> is not.

Requirements on class type T

If T is class type then it shall satisfy the following requirements. In this subclause t denotes an lvalue of type T, ct denotes as_const(t).

T and const T shall be contextually convertible to bool.

If T is implicitly convertible to element_type*, (element_type*)t == t.get() shall be true.

If const T is implicitly convertible to const element_type*, (const element_type*)ct == ct.get() shall be true.

Requirements on class types T
Expression Return type Pre-conditions Operational semantics
t.get() element_type*
ct.get() const element_type* or element_type* t.get() == ct.get().
*t element_type& t.get() != nullptr *t refers to the same object as *(t.get())
*ct const element_type& or element_type& ct.get() != nullptr *ct refers to the same object as *(ct.get())
t.operator->() element_type* t.get() != nullptr t.operator->() == t.get()
ct.operator->() const element_type* or element_type* ct.get() != nullptr ct.operator->() == ct.get()
(bool)t bool (bool)t is equivalent to t.get() != nullptr
(bool)ct bool (bool)ct is equivalent to ct.get() != nullptr

Constructors

template <class U> explicit(!is_convertible_v<U, T>) constexpr propagate_const(propagate_const<U>&& pu); is_constructible_v<T, U> is true. Initializes t_ as if direct-non-list-initializing an object of type T with the expression std::move(pu.t_). template <class U> explicit(!is_convertible_v<U, T>) constexpr propagate_const(U&& u); is_constructible_v<T, U> is true and decay_t<U> is not a specialization of propagate_const. Initializes t_ as if direct-non-list-initializing an object of type T with the expression std::forward<U>(u).

Assignment

template <class U> constexpr propagate_const& operator=(propagate_const<U>&& pu); U is implicitly convertible to T. t_ = std::move(pu.t_). *this. template <class U> constexpr propagate_const& operator=(U&& u); U is implicitly convertible to T and decay_t<U> is not a specialization of propagate_const. t_ = std::forward<U>(u). *this.

Const observers

explicit constexpr operator bool() const; (bool)t_. constexpr const element_type* operator->() const; get() != nullptr. get(). constexpr operator const element_type*() const; T is an object pointer type or has an implicit conversion to const element_type*. get(). constexpr const element_type& operator*() const; get() != nullptr. *get(). constexpr const element_type* get() const; t_ if T is an object pointer type, otherwise t_.get().

Non-const observers

constexpr element_type* operator->(); get() != nullptr. get(). constexpr operator element_type*(); T is an object pointer type or has an implicit conversion to element_type*. get(). constexpr element_type& operator*(); get() != nullptr. *get(). constexpr element_type* get(); t_ if T is an object pointer type, otherwise t_.get().

Modifiers

constexpr void swap(propagate_const& pt) noexcept(is_nothrow_swappable<T>); Lvalues of type T are swappable (). swap(t_, pt.t_).

Relational operators

template <class T> constexpr bool operator==(const propagate_const<T>& pt, nullptr_t); pt.t_ == nullptr. template <class T> constexpr bool operator==(nullptr_t, const propagate_const<T>& pt); nullptr == pt.t_. template <class T> constexpr bool operator!=(const propagate_const<T>& pt, nullptr_t); pt.t_ != nullptr. template <class T> constexpr bool operator!=(nullptr_t, const propagate_const<T>& pt); nullptr != pt.t_. template <class T, class U> constexpr bool operator==(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ == pu.t_. template <class T, class U> constexpr bool operator!=(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ != pu.t_. template <class T, class U> constexpr bool operator<(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ < pu.t_. template <class T, class U> constexpr bool operator>(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ > pu.t_. template <class T, class U> constexpr bool operator<=(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ <= pu.t_. template <class T, class U> constexpr bool operator>=(const propagate_const<T>& pt, const propagate_const<U>& pu); pt.t_ >= pu.t_. template <class T, class U> constexpr bool operator==(const propagate_const<T>& pt, const U& u); pt.t_ == u. template <class T, class U> constexpr bool operator!=(const propagate_const<T>& pt, const U& u); pt.t_ != u. template <class T, class U> constexpr bool operator<(const propagate_const<T>& pt, const U& u); pt.t_ < u. template <class T, class U> constexpr bool operator>(const propagate_const<T>& pt, const U& u); pt.t_ > u. template <class T, class U> constexpr bool operator<=(const propagate_const<T>& pt, const U& u); pt.t_ <= u. template <class T, class U> constexpr bool operator>=(const propagate_const<T>& pt, const U& u); pt.t_ >= u. template <class T, class U> constexpr bool operator==(const T& t, const propagate_const<U>& pu); t == pu.t_. template <class T, class U> constexpr bool operator!=(const T& t, const propagate_const<U>& pu); t != pu.t_. template <class T, class U> constexpr bool operator<(const T& t, const propagate_const<U>& pu); t < pu.t_. template <class T, class U> constexpr bool operator>(const T& t, const propagate_const<U>& pu); t > pu.t_. template <class T, class U> constexpr bool operator<=(const T& t, const propagate_const<U>& pu); t <= pu.t_. template <class T, class U> constexpr bool operator>=(const T& t, const propagate_const<U>& pu); t >= pu.t_.

Specialized algorithms

template <class T> constexpr void swap(propagate_const<T>& pt1, propagate_const<T>& pt2) noexcept(see below); is_swappable_v<T> is true. Equivalent to: pt1.swap(pt2). The expression inside noexcept is equivalent to:
noexcept(pt1.swap(pt2))

Underlying pointer access

Access to the underlying object pointer type is through free functions rather than member functions. These functions are intended to resemble cast operations to encourage caution when using them.

template <class T> constexpr const T& get_underlying(const propagate_const<T>& pt) noexcept; a reference to the underlying object pointer type. template <class T> constexpr T& get_underlying(propagate_const<T>& pt) noexcept; a reference to the underlying object pointer type.

Hash support

template <class T> struct hash<experimental::fundamentals_v3::propagate_const<T>>;

The specialization hash<experimental::fundamentals_v3::propagate_const<T>> is enabled () if and only if hash<T> is enabled. When enabled, for an object p of type propagate_const<T>, hash<experimental::fundamentals_v3::propagate_const<T>>()(p) evaluates to the same value as hash<T>()(p.t_).

Comparison function objects

template <class T> struct equal_to<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, equal_to<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as equal_to<T>()(p.t_, q.t_).

The specialization equal_to<T> is well-formed. The specialization equal_to<T> is well-defined.
template <class T> struct not_equal_to<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, not_equal_to<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as not_equal_to<T>()(p.t_, q.t_).

The specialization not_equal_to<T> is well-formed. The specialization not_equal_to<T> is well-defined.
template <class T> struct less<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, less<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as less<T>()(p.t_, q.t_).

The specialization less<T> is well-formed. The specialization less<T> is well-defined.
template <class T> struct greater<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, greater<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as greater<T>()(p.t_, q.t_).

The specialization greater<T> is well-formed. The specialization greater<T> is well-defined.
template <class T> struct less_equal<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, less_equal<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as less_equal<T>()(p.t_, q.t_).

The specialization less_equal<T> is well-formed. The specialization less_equal<T> is well-defined.
template <class T> struct greater_equal<experimental::fundamentals_v3::propagate_const<T>>;

For objects p, q of type propagate_const<T>, greater_equal<experimental::fundamentals_v3::propagate_const<T>>()(p, q) shall evaluate to the same value as greater_equal<T>()(p.t_, q.t_).

The specialization greater_equal<T> is well-formed. The specialization greater_equal<T> is well-defined.

Scope guard support

Header <experimental/scope> synopsis

namespace std::experimental::inline fundamentals_v3 {

  
  template <class EF>
    class scope_exit;
  template <class EF>
    class scope_fail;
  template <class EF>
    class scope_success;

  
  template <class R, class D>
    class unique_resource;

  
  template <class R, class D, class S=decay_t<R>>
    unique_resource<decay_t<R>, decay_t<D>>
      make_unique_resource_checked(
        R&& r, const S& invalid, D&& d) noexcept(see below);

} // namespace std::experimental::inline fundamentals_v3

Class templates scope_exit, scope_fail, and scope_success

The class templates scope_exit, scope_fail, and scope_success define scope guards that wrap a function object to be called on their destruction.

In this subclause, the placeholder scope-guard denotes each of these class templates. In descriptions of the class members, scope-guard refers to the enclosing class.

namespace std::experimental::inline fundamentals_v3 {

  template <class EF> class scope-guard {
  public:
    template <class EFP>
      explicit scope-guard(EFP&& f) noexcept(see below);
    scope-guard(scope-guard&& rhs) noexcept(see below);

    scope-guard(const scope-guard&) = delete;
    scope-guard& operator=(const scope-guard&) = delete;
    scope-guard& operator=(scope-guard&&) = delete;

    ~scope-guard () noexcept(see below);

    void release() noexcept;

  private:
    EF exit_function;                                 // exposition only
    bool execute_on_destruction{true};                // exposition only
    int uncaught_on_creation{uncaught_exceptions()};  // exposition only
  };

  template <class EF>
    scope-guard(EF) -> scope-guard<EF>;

}  // namespace std::experimental::inline fundamentals_v3

The class template scope_exit is a general-purpose scope guard that calls its exit function when a scope is exited. The class templates scope_fail and scope_success share the scope_exit interface, only the situation when the exit function is called differs.

void grow(vector<int>& v) {
  scope_success guard([]{ cout << "Good!" << endl; });
  v.resize(1024);
}

If the exit function object of a scope_success or scope_exit object refers to a local variable of the function where it is defined, e.g., as a lambda capturing the variable by reference, and that variable is used as a return operand in that function, it is possible for that variable to already have been returned when the scope-guard’s destructor executes, calling the exit function. This can lead to surprising behavior.

Template argument EF shall be a function object type (), lvalue reference to function, or lvalue reference to function object type. If EF is an object type, it shall meet the Cpp17Destructible requirements (C++20 Table 30). Given an lvalue g of type remove_reference_t<EF>, the expression g() shall be well-formed.

The constructor parameter f in the following constructors shall be a reference to a function or a reference to a function object ().

template <class EFP> explicit scope-guard(EFP&& f) noexcept( is_nothrow_constructible_v<EF, EFP> || is_nothrow_constructible_v<EF, EFP&>); is_same_v<remove_cvref_t<EFP>, scope-guard> is false and is_constructible_v<EF, EFP> is true. The expression f() is well-formed. Calling f() has well-defined behavior. For scope_exit and scope_fail, calling f() does not throw an exception. If EFP is not an lvalue reference type and is_nothrow_constructible_v<EF, EFP> is true, initialize exit_function with std::forward<EFP>(f); otherwise initialize exit_function with f. For scope_exit and scope_fail, if the initialization of exit_function throws an exception, calls f(). For scope_success, f() will not be called if the initialization fails. Any exception thrown during the initialization of exit_function. scope-guard(scope-guard&& rhs) noexcept(see below) (is_nothrow_move_constructible_v<EF> || is_copy_constructible_v<EF>) is true. If EF is an object type:
  • if is_nothrow_move_constructible_v<EF> is true, EF meets the Cpp17MoveConstructible requirements (C++20 Table 26),
  • otherwise EF meets the Cpp17CopyConstructible requirements (C++20 Table 27).
If is_nothrow_move_constructible_v<EF> is true, initializes exit_function with std::forward<EF>(rhs.exit_function), otherwise initializes exit_function with rhs.exit_function. Initializes execute_on_destruction from rhs.execute_on_destruction and uncaught_on_creation from rhs.uncaught_on_creation. If construction succeeds, call rhs.release(). Copying instead of moving provides the strong exception guarantee. execute_on_destruction yields the value rhs.execute_on_destruction yielded before the construction. uncaught_on_creation yields the value rhs.uncaught_on_creation yielded before the construction. Any exception thrown during the initialization of exit_function. The expression inside noexcept is equivalent to:
is_nothrow_move_constructible_v<EF> || is_nothrow_copy_constructible_v<EF>
~scope_exit() noexcept(true); Equivalent to:
if (execute_on_destruction)
  exit_function();
~scope_fail() noexcept(true); Equivalent to:
if (execute_on_destruction && uncaught_exceptions() > uncaught_on_creation)
  exit_function();
~scope_success() noexcept(noexcept(exit_function())); Equivalent to:
if (execute_on_destruction && uncaught_exceptions() <= uncaught_on_creation)
  exit_function();
If noexcept(exit_function()) is false, exit_function() can throw an exception, notwithstanding the restrictions of .
Any exception thrown by exit_function().
void release() noexcept; Equivalent to execute_on_destruction = false.

Class template unique_resource

Overview

namespace std::experimental::inline fundamentals_v3 {

  template <class R, class D> class unique_resource {
  public:
    
    unique_resource();
    template <class RR, class DD>
      unique_resource(RR&& r, DD&& d) noexcept(see below);
    unique_resource(unique_resource&& rhs) noexcept(see below);

    
    ~unique_resource();

    
    unique_resource& operator=(unique_resource&& rhs) noexcept(see below);

    
    void reset() noexcept;
    template <class RR>
      void reset(RR&& r);
    void release() noexcept;
    const R& get() const noexcept;
    see below operator*() const noexcept;
    R operator->() const noexcept;
    const D& get_deleter() const noexcept;

  private:
    using R1 = conditional_t<is_reference_v<
      R>, reference_wrapper<remove_reference_t<R>>, R>;  // exposition only

    R1 resource;                  // exposition only
    D deleter;                    // exposition only
    bool execute_on_reset{true};  // exposition only
  };

  template<class R, class D>
    unique_resource(R, D) -> unique_resource<R, D>;

}  // namespace std::experimental::inline fundamentals_v3

unique_resource is a universal RAII wrapper for resource handles. Typically, such resource handles are of trivial type and come with a factory function and a clean-up or deleter function that do not throw exceptions. The clean-up function together with the result of the creation function is used to create a unique_resource variable, that on destruction will call the clean-up function. Access to the underlying resource handle is achieved through get() and in case of a pointer type resource through a set of convenience pointer operator functions.

The template argument D shall meet the requirements of a Cpp17Destructible (C++20 Table 30) function object type (), for which, given a lvalue d of type D and a lvalue r of type R, the expression d(r) shall be well-formed. D shall either meet the Cpp17CopyConstructible requirements (C++20 Table 27), or D shall meet the Cpp17MoveConstructible requirements (C++20 Table 26) and is_nothrow_move_constructible_v<D> shall be true.

For the purpose of this subclause, a resource type T is an object type that meets the requirements of Cpp17CopyConstructible (C++20 Table 27), or is an object type that meets the requirements of Cpp17MoveConstructible (C++20 Table 26) and is_nothrow_move_constructible_v<T> is true, or is an lvalue reference to a resource type. R shall be a resource type.

For the scope of the adjacent subclauses, let RESOURCE be defined as follows:

  • resource.get() if is_reference_v<R> is true,
  • resource otherwise.

Constructors

unique_resource() is_default_constructible_v<R> && is_default_constructible_v<D> is true. Value-initializes resource and deleter; execute_on_reset is initialized with false. template <class RR, class DD> unique_resource(RR&& r, DD&& d) noexcept(see below)
is_constructible_v<R1, RR> &&
is_constructible_v<D , DD> &&
(is_nothrow_constructible_v<R1, RR> || is_constructible_v<R1,RR&>) &&
(is_nothrow_constructible_v<D , DD> || is_constructible_v<D ,DD&>)
is true. The first two conditions prohibit initialization from an rvalue reference when either R1 or D is a specialization of reference_wrapper.
The expressions d(r), d(RESOURCE) and deleter(RESOURCE) are well-formed. Calling d(r), d(RESOURCE) or deleter(RESOURCE) has well-defined behavior and does not throw an exception. If is_nothrow_constructible_v<R1, RR> is true, initializes resource with std::forward<RR>(r), otherwise initializes resource with r. Then, if is_nothrow_constructible_v<D, DD> is true, initializes deleter with std::forward<DD>(d), otherwise initializes deleter with d. If initialization of resource throws an exception, calls d(r). If initialization of deleter throws an exception, calls d(RESOURCE). The explained mechanism ensures no leaking of resources. Any exception thrown during initialization of resource or deleter. The expression inside noexcept is equivalent to:
(is_nothrow_constructible_v<R1, RR> || is_nothrow_constructible_v<R1, RR&>) &&
(is_nothrow_constructible_v<D , DD> || is_nothrow_constructible_v<D , DD&>)
unique_resource(unique_resource&& rhs) noexcept(see below); First, initialize resource as follows:
  • If is_nothrow_move_constructible_v<R1> is true, from std::move(rhs.resource);
  • otherwise, from rhs.resource.
If initialization of resource throws an exception, rhs is left owning the resource and will free it in due time. Then, initialize deleter as follows:
  • If is_nothrow_move_constructible_v<D> is true, from std::move(rhs.deleter);
  • otherwise, from rhs.deleter.
If initialization of deleter throws an exception and is_nothrow_move_constructible_v<R1> is true and rhs.execute_on_reset is true:
rhs.deleter(RESOURCE);
rhs.release();
Finally, execute_on_reset is initialized with exchange(rhs.execute_on_reset, false). The explained mechanism ensures no leaking and no double release of resources.
The expression inside noexcept is equivalent to:
is_nothrow_move_constructible_v<R1> && is_nothrow_move_constructible_v<D>

Destructor

~unique_resource(); Equivalent to reset().

Assignment

unique_resource& operator=(unique_resource&& rhs) noexcept(see below); If is_nothrow_move_assignable_v<R1> is true, R1 meets the Cpp17MoveAssignable (C++20 Table 28) requirements; otherwise R1 meets the Cpp17CopyAssignable (C++20 Table 29) requirements. If is_nothrow_move_assignable_v<D> is true, D meets the Cpp17MoveAssignable (C++20 Table 28) requirements; otherwise D meets the Cpp17CopyAssignable (C++20 Table 29) requirements. Equivalent to:
reset();
if constexpr (is_nothrow_move_assignable_v<R1>) {
  if constexpr (is_nothrow_move_assignable_v<D>) {
    resource = std::move(rhs.resource);
    deleter = std::move(rhs.deleter);
  } else {
    deleter = rhs.deleter;
    resource = std::move(rhs.resource);
  }
} else {
  if constexpr (is_nothrow_move_assignable_v<D>) {
    resource = rhs.resource;
    deleter = std::move(rhs.deleter);
  } else {
    resource = rhs.resource;
    deleter = rhs.deleter;
  }
}
execute_on_reset = exchange(rhs.execute_on_reset, false);
If a copy of a member throws an exception, this mechanism leaves rhs intact and *this in the released state.
*this. Any exception thrown during a copy-assignment of a member that cannot be moved without an exception. The expression inside noexcept is equivalent to:
is_nothrow_move_assignable_v<R1> && is_nothrow_move_assignable_v<D>

Other member functions

void reset() noexcept; Equivalent to:
if (execute_on_reset) {
  execute_on_reset = false;
  deleter(RESOURCE);
}
template <class RR> void reset(RR&& r); the selected assignment expression statement assigning resource is well-formed. The expression deleter(r) is well-formed. Calling deleter(r) has well-defined behavior and does not throw an exception. Equivalent to:
reset();
if constexpr (is_nothrow_assignable_v<R1&, RR>) {
  resource = std::forward<RR>(r);
} else {
  resource = as_const(r);
}
execute_on_reset = true;
If copy-assignment of resource throws an exception, calls deleter(r).
void release() noexcept; Equivalent to execute_on_reset = false. const R& get() const noexcept; resource. see below operator*() const noexcept; is_pointer_v<R> is true and is_void_v<remove_pointer_t<R>> is false. Equivalent to: return *get(); The return type is add_lvalue_reference_t<remove_pointer_t<R>>. R operator->() const noexcept; is_pointer_v<R> is true. get(). const D& get_deleter() const noexcept; deleter.

unique_resource creation

template <class R, class D, class S=decay_t<R>> unique_resource<decay_t<R>, decay_t<D>> make_unique_resource_checked(R&& resource, const S& invalid, D&& d) noexcept(is_nothrow_constructible_v<decay_t<R>, R> && is_nothrow_constructible_v<decay_t<D>, D>); The expression (resource == invalid ? true : false) is well-formed. Evaluation of the expression (resource == invalid ? true : false) has well-defined behavior and does not throw an exception. Returns an object constructed with members initialized from std::forward<R>(resource), std::forward<D>(d), and !bool(resource == invalid). Any failure during construction of the return value will not call d(resource) if bool(resource == invalid) is true.

This creation function exists to avoid calling a deleter function with an invalid argument.

The following example shows its use to avoid calling fclose when fopen fails.
auto file = make_unique_resource_checked(
    ::fopen("potentially_nonexistent_file.txt", "r"),
    nullptr,
    [](auto fptr){ ::fclose(fptr); });

Metaprogramming and type traits

Header <experimental/type_traits> synopsis

#include <type_traits>

namespace std::experimental::inline fundamentals_v3 {

  
  template <class> class invocation_type; // not defined
  template <class F, class... ArgTypes> class invocation_type<F(ArgTypes...)>;
  template <class> class raw_invocation_type; // not defined
  template <class F, class... ArgTypes> class raw_invocation_type<F(ArgTypes...)>;

  template <class T>
    using invocation_type_t = typename invocation_type<T>::type;
  template <class T>
    using raw_invocation_type_t = typename raw_invocation_type<T>::type;

  
  struct nonesuch;

  template <template<class...> class Op, class... Args>
    using is_detected = see below;
  template <template<class...> class Op, class... Args>
    inline constexpr bool is_detected_v
      = is_detected<Op, Args...>::value;
  template <template<class...> class Op, class... Args>
    using detected_t = see below;
  template <class Default, template<class...> class Op, class... Args>
    using detected_or = see below;
  template <class Default, template<class...> class Op, class... Args>
    using detected_or_t = typename detected_or<Default, Op, Args...>::type;
  template <class Expected, template<class...> class Op, class... Args>
    using is_detected_exact = is_same<Expected, detected_t<Op, Args...>>;
  template <class Expected, template<class...> class Op, class... Args>
    inline constexpr bool is_detected_exact_v
      = is_detected_exact<Expected, Op, Args...>::value;
  template <class To, template<class...> class Op, class... Args>
    using is_detected_convertible = is_convertible<detected_t<Op, Args...>, To>;
  template <class To, template<class...> class Op, class... Args>
    inline constexpr bool is_detected_convertible_v
      = is_detected_convertible<To, Op, Args...>::value;

} // namespace std::experimental::inline fundamentals_v3

Other type transformations

This subclause contains templates that may be used to transform one type to another following some predefined rule.

Each of the templates in this subclause shall be a TransformationTrait ().

Within this section, define the invocation parameters of INVOKE(f, t1, t2, ..., tN) as follows, in which T1 is the possibly cv-qualified type of t1 and U1 denotes T1& if t1 is an lvalue or T1&& if t1 is an rvalue:

  • When f is a pointer to a member function of a class T the invocation parameters are U1 followed by the parameters of f matched by t2, ..., tN.
  • When N == 1 and f is a pointer to member data of a class T the invocation parameter is U1.
  • If f is a class object, the invocation parameters are the parameters matching t1, ..., tN of the best viable function () for the arguments t1, ..., tN among the function call operators and surrogate call functions of f.
  • In all other cases, the invocation parameters are the parameters of f matching t1, ... tN.

In all of the above cases, if an argument tI matches the ellipsis in the function's parameter-declaration-clause, the corresponding invocation parameter is defined to be the result of applying the default argument promotions () to tI.

Assume S is defined as
struct S {
  int f(double const &) const;
  void operator()(int, int);
  void operator()(char const *, int i = 2, int j = 3);
  void operator()(...);
};
  • The invocation parameters of INVOKE(&S::f, S(), 3.5) are (S &&, double const &).
  • The invocation parameters of INVOKE(S(), 1, 2) are (int, int).
  • The invocation parameters of INVOKE(S(), "abc", 5) are (const char *, int). The defaulted parameter j does not correspond to an argument.
  • The invocation parameters of INVOKE(S(), locale(), 5) are (locale, int). Arguments corresponding to ellipsis maintain their types.
Other type transformations
TemplateConditionComments
template <class Fn, class... ArgTypes>
struct raw_invocation_type<Fn(ArgTypes...)>;
Fn and all types in the parameter pack ArgTypes shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound. see below
template <class Fn, class... ArgTypes>
struct invocation_type<Fn(ArgTypes...)>;
Fn and all types in the parameter pack ArgTypes shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound. see below

Access checking is performed as if in a context unrelated to Fn and ArgTypes. Only the validity of the immediate context of the expression is considered. The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed.

The member raw_invocation_type<Fn(ArgTypes...)>::type shall be defined as follows. If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is ill-formed when treated as an unevaluated operand (), there shall be no member type. Otherwise:

  • Let R denote result_of_t<Fn(ArgTypes...)>.
  • Let the types Ti be the invocation parameters of INVOKE(declval<Fn>(), declval<ArgTypes>()...).
  • Then the member type shall name the function type R(T1, T2, ...).

The member invocation_type<Fn(ArgTypes...)>::type shall be defined as follows. If raw_invocation_type<Fn(ArgTypes...)>::type does not exist, there shall be no member type. Otherwise:

  • Let A1, A2, … denote ArgTypes...
  • Let R(T1, T2, …) denote raw_invocation_type_t<Fn(ArgTypes...)>
  • Then the member type shall name the function type R(U1, U2, …) where Ui is decay_t<Ai> if declval<Ai>() is an rvalue otherwise Ti.

Detection idiom

struct nonesuch {
  ~nonesuch() = delete;
  nonesuch(nonesuch const&) = delete;
  void operator=(nonesuch const&) = delete;
};

nonesuch has no default constructor () or initializer-list constructor (), and is not an aggregate ().

template <class Default, class AlwaysVoid,
          template<class...> class Op, class... Args>
struct DETECTOR { // exposition only
  using value_t = false_type;
  using type = Default;
};

template <class Default, template<class...> class Op, class... Args>
struct DETECTOR<Default, void_t<Op<Args...>>, Op, Args...> { // exposition only
  using value_t = true_type;
  using type = Op<Args...>;
};

template <template<class...> class Op, class... Args>
  using is_detected = typename DETECTOR<nonesuch, void, Op, Args...>::value_t;

template <template<class...> class Op, class... Args>
  using detected_t = typename DETECTOR<nonesuch, void, Op, Args...>::type;

template <class Default, template<class...> class Op, class... Args>
  using detected_or = DETECTOR<Default, void, Op, Args...>;
// archetypal helper alias for a copy assignment operation:
template <class T>
  using copy_assign_t = decltype(declval<T&>() = declval<T const &>());

// plausible implementation for the is_assignable type trait:
template <class T>
  using is_copy_assignable = is_detected<copy_assign_t, T>;

// plausible implementation for an augmented is_assignable type trait
// that also checks the return type:
template <class T>
  using is_canonical_copy_assignable = is_detected_exact<T&, copy_assign_t, T>;
// archetypal helper alias for a particular type member:
template <class T>
  using diff_t = typename T::difference_type;

// alias the type member, if it exists, otherwise alias ptrdiff_t:
template <class Ptr>
  using difference_type = detected_or_t<ptrdiff_t, diff_t, Ptr>;