Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:

# CMake linting and formatting
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
rev: 0.27.6
rev: 0.27.7
hooks:
- id: gersemi
name: CMake linting
Expand Down
107 changes: 51 additions & 56 deletions include/beman/expected/expected.hpp

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions include/beman/expected/unexpected.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,9 @@ class unexpected<E&> {
// Binds E& directly to the referenced object; deleted below when G would bind to a temporary.
template <class G = E>
requires(!std::is_same_v<std::remove_cvref_t<G>, unexpected> &&
!std::is_same_v<std::remove_cvref_t<G>, std::in_place_t> && std::is_constructible_v<E&, G&&> &&
!std::is_same_v<std::remove_cvref_t<G>, std::in_place_t> && std::is_constructible_v<E&, G &&> &&
!detail::reference_constructs_from_temporary_v<E&, G>)
constexpr explicit unexpected(G&& e) noexcept
: ptr_(std::addressof(static_cast<E&>(std::forward<G>(e)))) {}
constexpr explicit unexpected(G&& e) noexcept : ptr_(std::addressof(static_cast<E&>(std::forward<G>(e)))) {}

// Deleted: binding would dangle (G materializes a temporary)
template <class G>
Expand All @@ -138,7 +137,7 @@ class unexpected<E&> {
// Deleted catch-all: neither constructible nor a dangling case
template <class G>
requires(!std::is_same_v<std::remove_cvref_t<G>, unexpected> &&
!std::is_same_v<std::remove_cvref_t<G>, std::in_place_t> && !std::is_constructible_v<E&, G&&> &&
!std::is_same_v<std::remove_cvref_t<G>, std::in_place_t> && !std::is_constructible_v<E&, G &&> &&
!detail::reference_constructs_from_temporary_v<E&, G>)
constexpr unexpected(G&&) = delete;

Expand All @@ -147,7 +146,7 @@ class unexpected<E&> {
// reference or not. Naturally restricted to arity 1: there is no variadic overload here,
// and expected only ever calls this when is_constructible_v<E&, Args...> already holds.
template <class G = E>
requires(std::is_constructible_v<E&, G&&> && !detail::reference_constructs_from_temporary_v<E&, G>)
requires(std::is_constructible_v<E&, G &&> && !detail::reference_constructs_from_temporary_v<E&, G>)
constexpr explicit unexpected(std::in_place_t, G&& e) noexcept
: ptr_(std::addressof(static_cast<E&>(std::forward<G>(e)))) {}

Expand Down
157 changes: 53 additions & 104 deletions papers/wg21-latex/implementation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ template <class T>
class optional<T&> {
public:
using value_type = T;
using iterator =
std::contiguous_iterator<T,
optional>; // see [optionalref.iterators]
using iterator = std::contiguous_iterator<T,
optional>; // see [optionalref.iterators]
public:
// \ref{optionalref.ctor}, constructors

Expand All @@ -21,13 +20,11 @@ class optional<T&> {
constexpr optional(const optional& rhs) noexcept = default;

template <class Arg>
requires(std::is_constructible_v<T&, Arg> &&
!std::reference_constructs_from_temporary_v<T&, Arg>)
requires(std::is_constructible_v<T&, Arg> && !std::reference_constructs_from_temporary_v<T&, Arg>)
constexpr explicit optional(in_place_t, Arg&& arg);

template <class U>
requires(std::is_constructible_v<T&, U> &&
!(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
requires(std::is_constructible_v<T&, U> && !(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
!(std::is_same_v<std::remove_cvref_t<U>, optional>) &&
!std::reference_constructs_from_temporary_v<T&, U>)
constexpr explicit(!std::is_convertible_v<U, T&>)
Expand All @@ -36,8 +33,7 @@ class optional<T&> {
}

template <class U>
requires(std::is_constructible_v<T&, U> &&
!(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
requires(std::is_constructible_v<T&, U> && !(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
!(std::is_same_v<std::remove_cvref_t<U>, optional>) &&
std::reference_constructs_from_temporary_v<T&, U>)
constexpr optional(U&& u) = delete;
Expand All @@ -47,66 +43,47 @@ class optional<T&> {
// allows correct constraints by propagating the value category from the
// optional to the value within the rhs.
template <class U>
requires(std::is_constructible_v<T&, U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U&>)
constexpr explicit(!std::is_convertible_v<U&, T&>) optional(
optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, U&>);
requires(std::is_constructible_v<T&, U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U&>)
constexpr explicit(!std::is_convertible_v<U&, T&>)
optional(optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, U&>);

template <class U>
requires(std::is_constructible_v<T&, const U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, const U&>)
requires(std::is_constructible_v<T&, const U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, const U&>)
constexpr explicit(!std::is_convertible_v<const U&, T&>)
optional(const optional<U>& rhs) noexcept(
std::is_nothrow_constructible_v<T&, const U&>);
optional(const optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, const U&>);

template <class U>
requires(std::is_constructible_v<T&, U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U>)
requires(std::is_constructible_v<T&, U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U>)
constexpr explicit(!std::is_convertible_v<U, T&>)
optional(optional<U>&& rhs) noexcept(
noexcept(std::is_nothrow_constructible_v<T&, U>));
optional(optional<U>&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v<T&, U>));

template <class U>
requires(std::is_constructible_v<T&, const U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, const U>)
requires(std::is_constructible_v<T&, const U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, const U>)
constexpr explicit(!std::is_convertible_v<const U, T&>)
optional(const optional<U>&& rhs) noexcept(
noexcept(std::is_nothrow_constructible_v<T&, const U>));
optional(const optional<U>&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v<T&, const U>));

template <class U>
requires(std::is_constructible_v<T&, U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
std::reference_constructs_from_temporary_v<T&, U&>)
requires(std::is_constructible_v<T&, U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && std::reference_constructs_from_temporary_v<T&, U&>)
constexpr optional(optional<U>& rhs) = delete;

template <class U>
requires(std::is_constructible_v<T&, const U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
std::reference_constructs_from_temporary_v<T&, const U&>)
requires(std::is_constructible_v<T&, const U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && std::reference_constructs_from_temporary_v<T&, const U&>)
constexpr optional(const optional<U>& rhs) = delete;

template <class U>
requires(std::is_constructible_v<T&, U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
std::reference_constructs_from_temporary_v<T&, U>)
requires(std::is_constructible_v<T&, U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && std::reference_constructs_from_temporary_v<T&, U>)
constexpr optional(optional<U>&& rhs) = delete;

template <class U>
requires(std::is_constructible_v<T&, const U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
std::reference_constructs_from_temporary_v<T&, const U>)
requires(std::is_constructible_v<T&, const U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && std::reference_constructs_from_temporary_v<T&, const U>)
constexpr optional(const optional<U>&& rhs) = delete;

// \ref{optionalref.dtor}, destructor
Expand All @@ -118,10 +95,8 @@ class optional<T&> {
constexpr optional& operator=(const optional& rhs) noexcept = default;

template <class U>
requires(std::is_constructible_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U>)
constexpr T&
emplace(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>);
requires(std::is_constructible_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U>)
constexpr T& emplace(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>);

// \ref{optionalref.swap}, swap
constexpr void swap(optional& rhs) noexcept;
Expand Down Expand Up @@ -167,8 +142,7 @@ class optional<T&> {
// \rSec3[optionalref.ctor]{Constructors}
template <class T>
template <class Arg>
requires(std::is_constructible_v<T&, Arg> &&
!std::reference_constructs_from_temporary_v<T&, Arg>)
requires(std::is_constructible_v<T&, Arg> && !std::reference_constructs_from_temporary_v<T&, Arg>)
constexpr optional<T&>::optional(in_place_t, Arg&& arg) {
convert_ref_init_val(std::forward<Arg>(arg));
}
Expand All @@ -187,49 +161,38 @@ constexpr optional<T&>::optional(in_place_t, Arg&& arg) {

template <class T>
template <class U>
requires(std::is_constructible_v<T&, U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U&>)
constexpr optional<T&>::optional(optional<U>& rhs) noexcept(
std::is_nothrow_constructible_v<T&, U&>) {
requires(std::is_constructible_v<T&, U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U&>)
constexpr optional<T&>::optional(optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, U&>) {
if (rhs.has_value()) {
convert_ref_init_val(*rhs);
}
}

template <class T>
template <class U>
requires(std::is_constructible_v<T&, const U&> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, const U&>)
constexpr optional<T&>::optional(const optional<U>& rhs) noexcept(
std::is_nothrow_constructible_v<T&, const U&>) {
requires(std::is_constructible_v<T&, const U&> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, const U&>)
constexpr optional<T&>::optional(const optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, const U&>) {
if (rhs.has_value()) {
convert_ref_init_val(*rhs);
}
}

template <class T>
template <class U>
requires(std::is_constructible_v<T&, U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U>)
constexpr optional<T&>::optional(optional<U>&& rhs) noexcept(
noexcept(std::is_nothrow_constructible_v<T&, U>)) {
requires(std::is_constructible_v<T&, U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U>)
constexpr optional<T&>::optional(optional<U>&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v<T&, U>)) {
if (rhs.has_value()) {
convert_ref_init_val(*std::move(rhs));
}
}

template <class T>
template <class U>
requires(std::is_constructible_v<T&, const U> &&
!std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, const U>)
requires(std::is_constructible_v<T&, const U> && !std::is_same_v<std::remove_cv_t<T>, optional<U>> &&
!std::is_same_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, const U>)
constexpr optional<T&>::optional(const optional<U>&& rhs) noexcept(
noexcept(std::is_nothrow_constructible_v<T&, const U>)) {
if (rhs.has_value()) {
Expand All @@ -246,10 +209,8 @@ constexpr optional<T&>& optional<T&>::operator=(nullopt_t) noexcept {

template <class T>
template <class U>
requires(std::is_constructible_v<T&, U> &&
!std::reference_constructs_from_temporary_v<T&, U>)
constexpr T&
optional<T&>::emplace(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>) {
requires(std::is_constructible_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U>)
constexpr T& optional<T&>::emplace(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>) {
convert_ref_init_val(std::forward<U>(u));
return *value_;
}
Expand Down Expand Up @@ -301,12 +262,9 @@ constexpr T& optional<T&>::value() const {
template <class T>
template <class U>
constexpr std::remove_cv_t<T> optional<T&>::value_or(U&& u) const {
static_assert(std::is_constructible_v<std::remove_cv_t<T>, T&>,
"T must be constructible from a T&");
static_assert(std::is_convertible_v<U, std::remove_cv_t<T>>,
"Must be able to convert u to T");
return has_value() ? *value_
: static_cast<std::remove_cv_t<T>>(std::forward<U>(u));
static_assert(std::is_constructible_v<std::remove_cv_t<T>, T&>, "T must be constructible from a T&");
static_assert(std::is_convertible_v<U, std::remove_cv_t<T>>, "Must be able to convert u to T");
return has_value() ? *value_ : static_cast<std::remove_cv_t<T>>(std::forward<U>(u));
}

// \rSec3[optionalref.monadic]{Monadic operations}
Expand All @@ -324,15 +282,11 @@ constexpr auto optional<T&>::and_then(F&& f) const {

template <class T>
template <class F>
constexpr optional<std::invoke_result_t<F, T&>>
optional<T&>::transform(F&& f) const {
constexpr optional<std::invoke_result_t<F, T&>> optional<T&>::transform(F&& f) const {
using U = std::invoke_result_t<F, T&>;
static_assert(!std::is_same_v<std::remove_cvref_t<U>, in_place_t>,
"Result must not be in_place_t");
static_assert(!std::is_same_v<std::remove_cvref_t<U>, nullopt_t>,
"Result must not be nullopt_t");
static_assert((std::is_object_v<U> && !std::is_array_v<U>) ||
std::is_lvalue_reference_v<U>,
static_assert(!std::is_same_v<std::remove_cvref_t<U>, in_place_t>, "Result must not be in_place_t");
static_assert(!std::is_same_v<std::remove_cvref_t<U>, nullopt_t>, "Result must not be nullopt_t");
static_assert((std::is_object_v<U> && !std::is_array_v<U>) || std::is_lvalue_reference_v<U>,
"Result must be an non-array object or an lvalue reference");
if (has_value()) {
return optional<U>{std::invoke(std::forward<F>(f), *value_)};
Expand All @@ -345,8 +299,7 @@ template <class T>
template <class F>
constexpr optional<T&> optional<T&>::or_else(F&& f) const {
using U = std::invoke_result_t<F>;
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>,
"Result must be an optional");
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>, "Result must be an optional");
if (has_value()) {
return *value_;
} else {
Expand All @@ -364,15 +317,11 @@ constexpr void optional<T&>::reset() noexcept {
namespace std {
template <typename T>
requires requires(T a) {
{
std::hash<remove_const_t<T>>{}(a)
} -> std::convertible_to<std::size_t>;
{ std::hash<remove_const_t<T>>{}(a) } -> std::convertible_to<std::size_t>;
}
struct hash<beman::optional::optional<T>> {
static_assert(!is_reference_v<T>,
"hash is not enabled for reference types");
size_t operator()(const beman::optional::optional<T>& o) const
noexcept(noexcept(hash<remove_const_t<T>>{}(*o))) {
static_assert(!is_reference_v<T>, "hash is not enabled for reference types");
size_t operator()(const beman::optional::optional<T>& o) const noexcept(noexcept(hash<remove_const_t<T>>{}(*o))) {
if (o) {
return std::hash<std::remove_const_t<T>>{}(*o);
} else {
Expand Down
5 changes: 4 additions & 1 deletion tests/beman/expected/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ if(NOT BEMAN_EXPECTED_USING_LIBCXX)
beman.expected.tests.expected.std
PRIVATE BEMAN_EXPECTED_TEST_STD
)
target_compile_features(beman.expected.tests.expected.std PRIVATE cxx_std_23)
target_compile_features(
beman.expected.tests.expected.std
PRIVATE cxx_std_23
)
target_include_directories(
beman.expected.tests.expected.std
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/tests
Expand Down
4 changes: 2 additions & 2 deletions tests/beman/expected/expected_ref.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,8 @@ TEST_CASE("expected<T&>: const transform", "[expected_ref]") {

TEST_CASE("expected<T&>: const transform on error - propagates", "[expected_ref]") {
const expected<int&, int> e(unexpect, 5);
bool called = false;
auto r = e.transform([&](int&) {
bool called = false;
auto r = e.transform([&](int&) {
called = true;
return 0;
});
Expand Down
Loading
Loading