Skip to content

<xstd/misc/concepts/proxy_reference.hpp>

xstd::proxy_reference

template<class R>
concept proxy_reference =
  requires(R& ref) { { &ref } -> proxy_semi_iterator; } and
  same_as<iter_reference_t<decltype(&declval<R&>())>, R> and
  indirectly_writable<decltype(&declval<R&>()), iter_value_t<decltype(&declval<R&>())>>;

A proxy whose address is a semi-iterator dereferencing back to it, and which writes through to its value when const.

Models

  • A library's bit reference models it exactly where the library gives it an operator& and an assignment through const.
  • A read-only proxy, such as vector<bool>::const_reference, does not.
  • A class that converts without the round trip does not.
  • An address that dereferences to the value rather than back to the proxy is turned away.

Notes

  • A type that models it is a semi-reference for the value type its address names, and its address models proxy_iterator.
  • It does not subsume proxy_semi_reference<R, V>, whose V the caller names: overloads constrained on each are ambiguous for a type that satisfies both, and and (not proxy_reference<R>) on the weaker one resolves them.

Example

#include <xstd/misc/concepts/proxy_reference.hpp> // proxy_reference
#include <cstddef>                                // ptrdiff_t
#include <iostream>                               // boolalpha, cout
#include <iterator>                               // iter_value_t
#include <vector>                                 // vector

enum class shade : unsigned char
{
        light,
        dark
};

class shade_iterator;

// One shade, kept as one bit of a word, and read and written through this proxy.
class shade_reference
{
        unsigned* m_word;
        unsigned m_bit;

public:
        [[nodiscard]] constexpr shade_reference(unsigned* word, unsigned bit) noexcept
                : m_word(word)
                , m_bit(bit)
        {}

        [[nodiscard]] constexpr explicit(false) operator shade() const noexcept
        {
                return static_cast<shade>((*m_word >> m_bit) & 1U);
        }

        // Writes through a const proxy, as a const language reference writes to what it refers to.
        constexpr auto operator=(shade value) const noexcept
                -> shade_reference const&
        {
                *m_word = (*m_word & ~(1U << m_bit)) | (static_cast<unsigned>(value) << m_bit);
                return *this;
        }

        // The address of a shade is an iterator that yields this proxy back.
        [[nodiscard]] constexpr auto operator&() const noexcept
                -> shade_iterator;
};

class shade_iterator
{
        unsigned* m_word;
        unsigned m_bit;

public:
        using value_type      = shade;
        using difference_type = std::ptrdiff_t;

        [[nodiscard]] constexpr shade_iterator(unsigned* word, unsigned bit) noexcept
                : m_word(word)
                , m_bit(bit)
        {}

        [[nodiscard]] constexpr auto operator*() const noexcept
                -> shade_reference
        {
                return shade_reference(m_word, m_bit);
        }
};

constexpr auto shade_reference::operator&() const noexcept
        -> shade_iterator
{
        return shade_iterator(m_word, m_bit);
}

[[nodiscard]] constexpr auto name(shade s) noexcept
        -> char const*
{
        return s == shade::dark ? "dark" : "light";
}

// Resets what a language reference refers to.
template<class T>
auto reset(T& value) -> void
{
        value = T();
}

// Resets what a proxy reference stands for: taken by value, it still writes through, and its address names the value.
template<xstd::proxy_reference R>
auto reset(R ref) -> void
{
        ref = std::iter_value_t<decltype(&ref)>();
}

auto main() -> int
{
        auto plain = shade::dark;
        auto word  = 0b11U;

        reset(plain);
        reset(*shade_iterator(&word, 1));
        std::cout << "plain is " << name(plain) << ", the packed shades are " << name(*shade_iterator(&word, 0)) << " and " << name(*shade_iterator(&word, 1)) << '\n';

        std::cout << std::boolalpha;
        std::cout << "proxy_reference<shade_reference> is " << xstd::proxy_reference<shade_reference> << '\n';
        std::cout << "proxy_reference<shade&> is " << xstd::proxy_reference<shade&> << '\n';
        std::cout << "proxy_reference<vector<bool>::const_reference> is " << xstd::proxy_reference<std::vector<bool>::const_reference> << '\n';
}

Output:

plain is light, the packed shades are dark and light
proxy_reference<shade_reference> is true
proxy_reference<shade&> is false
proxy_reference<vector<bool>::const_reference> is false

See also

Entity Relation
proxy_semi_reference the weaker concept, with the value type named by the caller
proxy_iterator what its address models
to_underlying takes a proxy reference to an enumerator