meta::writable

Defined in header <mgs/meta/concepts/writable.hpp>.

template <typename Out, typename T>
concept writable =
  requires(Out&& o, T&& t) {
    *o = std::forward<T>(t);
    *std::forward<Out>(o) = std::forward<T>(t);
    const_cast<meta::iter_reference_t<Out> const&&>(*o) = std::forward<T>(t);
    const_cast<meta::iter_reference_t<Out> const&&>(*std::forward<Out>(o)) = std::forward<T>(t);
  };

Pre-C++20 implementation of the std::writable concept.


This concept’s definition is really obscure. It deserves a more detailed explanation.

The last two requirements ensure that Out’s operator* does not return a prvalue, since writing to a temporary value is unlikely to be the intended behavior (except when Out is a proxy-type, e.g. std::vector<bool>::reference).

Casting the result of *o to std::iter_reference_t<Out> const&& does not have any effect if the result is a reference type (including rvalue references), due to reference collapsing.

But if a prvalue is returned, the result will be cast to a const rvalue reference. From there, that result type’s operator= must be a const member-function and overloaded on rvalue references.

If that is not the case, Out is not writable.

Note

Although std::vector<bool>::reference is a proxy-type, it does not model std::writable, because its operator= is not const.

Concept emulation

namespace mgs {
namespace meta {

template <typename Out, typename T>
struct is_writable { /* ... */ };

template <typename Out, typename T>
constexpr auto is_writable_v = is_writable<In>::value;

template <typename Out, typename T,
          typename = std::enable_if_t<is_writable_v<Out, T>>>
using writable = Out;

} // namespace meta
} // namespace mgs

Example

#include <mgs/meta/concepts/writable.hpp>

struct It {
  struct Proxy {
    Proxy operator*() const;

    Proxy& operator=(std::string const&&) const;
    Proxy& operator=(std::string&&) const;
    Proxy& operator=(std::string const&) const;
    Proxy& operator=(std::string&) const;

    Proxy& operator=(Proxy const&) = default;
    Proxy& operator=(Proxy&&) = default;

    operator std::string() const;

    // Imagine having a std::string* as member
  };

  using value_type = std::string;
  using pointer = Proxy*;
  using reference = Proxy;
  using difference_type = std::ptrdiff_t;
  using iterator_category = std::input_iterator_tag;

  reference operator*();
  It& operator++();
  It operator++(int);
};

static_assert(is_writable<char*, char>::value, "");
static_assert(!is_writable<char const*, char>::value, "");

static_assert(is_writable<It, std::string>::value, "");