jfbastien / papers Goto Github PK
View Code? Open in Web Editor NEWPapers for the C++ Standards Committee, WG21
Papers for the C++ Standards Committee, WG21
At
Line 1414 in fa8426b
the Remarks: uses is_convertible_v<Err, E>
and the Constraints: uses is_constructible_v<E, Err>
but in both cases it should be const Err&
not Err
, since the construction is from a const lvalue not an rvalue.
As I understand it, the fetch_add is turned into 2 instructions: mfence and mov
The text here claims it turns into just a mov
This x86 assembly reduces contention by replacing fetch_add—an instruction requiring exclusive cache line access—to a simple movl
Do we want really to support in-place construction for expected<void, E>
```c++
constexpr explicit expected<void, E>::expected(in_place_t);
```
as it has no added value respect the default constructor?
expected<void, E> x {in_place};
The current wording supports it and is coherent respect to
expected<void, E> x;
x.emplace();
template <class E>
class unexpected {
public:
unexpected() = delete;
constexpr unexpected(const unexpected&) = default;
constexpr unexpected(unexpected&&) = default;
template <class Err>
constexpr explicit unexpected(Err&&);
template <class Err>
constexpr EXPLICIT unexpected(const unexpected<Err>&);
template <class Err>
constexpr EXPLICIT unexpected(unexpected<Err>&&);
constexpr unexpected& operator=(const unexpected&);
constexpr unexpected& operator=(unexpected&&) noexcept(see below);
template <class Err>
constexpr unexpected& operator=(const unexpected<Err>&);
template <class Err>
constexpr unexpected& operator=(unexpected<Err>&&);
constexpr const E& value() const& noexcept;
constexpr E& value() & noexcept;
constexpr const E&& value() const&& noexcept;
constexpr E&& value() && noexcept;
void swap(unexpected&) noexcept(see below);
private:
E val; // exposition only
};
All functions returning expected<T,E>
should be declared [[nodiscard]]
or expected<T,E>
should be declared with attribute [[nodiscard]]
.
This is particular important for expected<void,E>
as the user could forget to check if there is an error as there is no value to retrieve.
Under Section 1.6
template<class U, class G> explicit(see below) constexpr expected(const expected<U, G>& rhs)
: Are the constraints involving E
and G
still applicable when T
and U
are cv void
?
template<class U, class G> explicit(see below) constexpr expected(expected<U, G>&& rhs)
: Are the constraints involving E
and G
still applicable when T
and U
are cv void
?
template<class... Args> constexpr explicit expected(unexpect_t, Args&&... args);
: Should be "with the arguments in_place, std::forward<Args>(args)....
"
template<class U, class... Args> constexpr explicit expected(unexpect_t, initializer_list<U> il, Args&&... args);
: Should be "with the arguments in_place, il, std::forward<Args>(args)....
"
Under Section 1.8
expected& operator=(const expected& rhs) noexcept(see below);
: noexcept specification does not match the one in the class synopsis.
expected& operator=(expected&& rhs) noexcept(see below);
move constructs an
unexpected_type<E> tmp
fromunexpected(std::move(this->error()))
(which can’t throw as E is nothrow-move-constructible)
For !bool(*this) && bool(rhs)
, missing "and set has_val
to true
"
Constraint for when T
is cv void should be is_move_constructible_v<E>
is true
and is_move_assignable_v<E>
is true
.
Constraint for when T
is not cv void should be (determined by following the logic in the table):
is_move_constructible_v<T> && is_move_assignable_v<T> && is_move_constructible_v<E> && is_move_assignable_v<E> && (is_nothrow_move_constructible_v<E> || is_nothrow_move_constructible_v<T>)
The expression inside noexcept should be equivalent to:
is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E>
template<class G = E> expected<T, E>& operator=(const unexpected<G>& e);
: Table is inverted
expected<T, E>& operator=(const unexpected<G>& e);
and expected<T, E>& operator=(unexpected<G>&& e);
Should be e.value()
instead of e.error()
.
Temporary unexpected<E>
should be created from unexpected<G>
before destroying val
to ensure strong exception guarantee.
expected<T, E>& operator=(unexpected<G>&& e);
, for !bool(*this)
: Missing std::move, should be "move assign unexpected(std::move(e).value())
to unex".
template<class U = T> expected<T, E>& operator=(U&& v);
: Missing std::move, should be
move constructs an
unexpected<E> tmp
fromunexpected(std::move(this->error()))
(which can’t throw as E is nothrow-move-constructible)
Why are there no assignment operator overloads taking expected<U, G>
? There are after all assignment operator overloads for std::optional taking std::optional<U>
.
template<class... Args> T& emplace(Args&&... args);
: By following the logic under Effects, the constraint should be is_constructible_v<T, Args...> && (is_nothrow_constructible_v<T, Args...> || is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E>)
. Upon exception, one or more arguments may be left in a post-moved state, so the "nothing changes" remark is not completely accurate.
template<class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);
: By following the logic under Effects, the constraint should be is_constructible_v<T, L, Args...> && (is_nothrow_constructible_v<T, L, Args...> || is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E>)
where L
is std::initializer_list<U>
. Upon exception, one or more arguments may be left in a post-moved state, so the "nothing changes" remark is not completely accurate.
Under Section 1.9
Constraints should be is_nothrow_move_constructible_v
, not is_move_constructible_v
.
In table, for bool(*this) && !bool(rhs)
and is cv void, should be unexpected(std::move(rhs.error()))
(missing .error()
)
Under Section 1.10
constexpr T&& expected::value() &&
and constexpr const T&& expected::value() const&&
: Should they throw bad_expected_access(std::move(*this).error())
instead, in order to allow move-only error types?
value_or
: Behavior and/or constraints need to be clarified when T
is void
. Does it ignore the argument and do nothing, or is it disabled entirely?
Under Section 1.12
operator!=
: Should be "expression *x != v
is well-formed.
operator!=
: Should be "Returns: bool(x) ? *x != v : true;
. If x
holds an error, than is it unequal to any value v
. Otherwise, !( x != v)
would not be the same as x == v
.
Under Section 1.14
Under Section 1.17.1
Should be Err
in constraints, instead of U
.
template<class Err> constexpr explicit unexpected(Err&& e)
: Should be unexpect_t
in constraints, not unexpected
.
Looks like you changed the first letter from D to P and now all the source
links on pages like these are broken:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html
if unexpected support heterogeneous conversions, it seems natural that it should support heterogeneous equality comparisions.
Sample code uses confusing double semicolon at the end of line:
ej = unexpected(E());;
ei = unexpected(E());;
Also draft R12 of specification still has this problem
Do we want immutable expected?
If yes, should we declare them as expected<const T, const E>
?
or it is better to have a specific const_expected<T,E>
?
Do we want so support volatile expected<volatile T,E>?
Granted we don't want to support references for the first version.
Are there other types we don't want to support?
expected<void,E> specialization change quite a lot the description of the normal template expected<T,E> and makes the wording even less clear.
Should we describe it as a separated template specialization?
Do we want unexpected to be instantiated with functions pointers, pointers, arrays, references, cv-qualified types, void?
IMO, we should allow any unqualified type that is not a reference and is not void.
At
Line 1196 in fa8426b
*Requires*: The expressions `*x == *y` and
`unexpected(x.error()) == unexpected(y.error())` shall be well-formed and its result
shall be convertible to `bool`.
*Returns*: If `bool(x) != bool(y)`, `false`; otherwise if `bool(x) == false`,
`x.error() == y.error()`; otherwise `true` if `T1` and `T2` are *cv* `void` or `*x == *y` otherwise.
The requirement that unexpected(x.error()) == unexpected(y.error())
is well-formed is inconsistent with doing x.error() == y.error()
.
Is p0323r7 adopted by c++23 ?
std::expected very good candidate for alternative exceptions error-code principle. But there access value methods throw the exception bad_access_expected: again the exception! std::expected already have has_value method to check this situation.
Yes, std::optional::value() also throws exception, but std::expected specially class for exception-free code, in my opinion.
I personally thought about, why can't to remove explicit throwing any exception ?
For example, std::move_only_function's call "empty" state no more throws bad_functional_call exception, it simply cause an UB.
Pls, remove bad_access_expected class, and do not throw an any exception, explicitly! Let value() cause an UB, if there error state.
I will suggest to replace them by success_value and failure_value.
The following signatures are ill formed or have no sense when T
void
.
constexpr const T* operator ->() const;
constexpr T* operator ->();
constexpr const T& operator *() const&;
constexpr T& operator *() &;
constexpr const T&& operator *() const &&;
constexpr T&& operator *() &&;
constexpr const T& value() const&;
constexpr T& value() &;
constexpr const T&& value() const &&;
constexpr T&& value() &&;
template <class U>
constexpr T value_or(U&&) const&;
template <class U>
constexpr T value_or(U&&) &&;
Either all should not participate in overload resolution for expected<void, E>
or maybe
expected<void::E>::value() const
.
I prefer to don't define them for expected<void, E>
. How to organize the wording then?
Should we use see below
for the ill formed parts?
template <class Err>
constexpr explicit unexpected(Err&& e);
Effects: Initializes val
as if direct-non-list-initializing an
object of type E
with the expression std::forward<Err>(e)
.
Remark: This constructor participates in overload resolution if and only if std::is_constructible_v<E, Err&&>
We need in addition
and is_same_v<remove_cvref_t<Err>, unexpected<E>>
is false
so that unexpected<E>
is trivial if E
is trivial.
Is this sample correct?
int x = 0;
std::atomic<int> y;
int no() {
x = 0;
y.store(0, std::memory_order_release);
while (!y.load(std::memory_order_acquire));
x = 1;
return z;
}
(From
Lines 376 to 388 in 0ef0303
z
is not defined. I'm not sure if it should be defined and then assigned in the loop or if it should just return 0
In the enumeration of the four suggested solutions, I feel that at least for the sake of discussion, a fifth solution, described in a sidebar within p0603r0, should be mentioned: change the memory model such that data races aren't undefined behaviour, but rather, that reads of raced data produce indeterminate values, the use of which is undefined behaviour.
p0603r0 says that this is likely to be too radical a change to the memory model, though it would not in general alter program correctness, as aside from these speculative examples programs do not generally read racy data without using the values that have been read.
Adopting such a rule in general would be problematic, due for example to trap values. But a constrained proposal--for example, "byte-wise reads (which are already guaranteed to not trap) do not introduce races even in the face of concurrent writes, they merely produce indeterminate values"--would be:
atomic<T>
for large T can be ignored (since these structures would not need atomic<T>
at all and hence we wouldn't care if atomic<T>
needed embedded locks on some platforms)memcpy
(since p0603r0's ultimate proposal, atomic<T>::nonatomic_Xxx
is not itself memcpy
)atomic<T>
non-atomic operations which is, at the very least, aesthetically displeasing.Wondering if we just need an const &
overload for error() as we have for system_error
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.