[variant2] Andrzej's review -- documentation
Hi Everyone,
This is the review of the documentation of variant2.
First, the docs assume that the user is already familiar with std::variant
or boost::variant, and the introductory part only highlights the difference
from std::variant. It does not even mention that variant
and Ti be the ith type in Types. http://eel.is/c++draft/variant.ctor#1.sentence-1
See http://eel.is/c++draft/variant.ctor#1 A similar definition should be put in variant2's documentation. Noexcept specifications use mp_all. this assumes that users of variant2 must be familiar with Boost.mp11. I recommend removing the reference to mp_all and instead use something similar to what the Standard does: http://eel.is/c++draft/variant.ctor#11 Specification of function `emplace<I>()`: It says:
On exception: If the list of alternatives contains monostate, the contained value is either unchanged, or monostate{};
In what situation on exception is the contained value left unchanged? This question applies to the next bullet as well. The specification also doesn't mention that `.emplace<I>()` requires Ti to be move-constructible in some cases. It also says: Throws: Nothing unless the initialization of the new contained value throws. Which sounds strange, because it does throw something. In other places you use "Throws: Any exception thrown by the initialization of the contained value.", which makes more sense. Also, the Standard uses, "Throws: Any exception thrown during the initialization of the contained value. http://eel.is/c++draft/variant.mod#11.sentence-1": http://eel.is/c++draft/variant.mod#11 swap(): What does it throw and when? Other functions specify it but this one does not. get<I>(): Specification says,
If v.index() is I, returns a reference to the object stored in the variant. Otherwise, throws bad_variant_access.
This means that if I provide an index that is far greater than `sizeof...(T)`, the program should compile fine and throw an exception at run-time. This is not what the implementation does: I get a compile-time error, which is good, but inconsistent with the docs. They need an additional "Requires:" clause. visit(): I think it is underspecified. It is not immediately clear that the first parameter is the visitor and the remaining ones are variants. Parameter names F and V do not convey this clearly. Either use Visitor and Variants... or provide a longer description, even if informal. The docs really should contain an example of `visit()` somewhere. According to the specs, the following should work: ``` void f() {} variant2::visit(f); ``` which is not incorrect, but strange. The std::variant also provides a second overload of `visit` that explicitly defines the return type: http://eel.is/c++draft/variant.visit This overload is not present in variant2. But this fact is not listed in the initial section of the docs that lists the differences between the two libraries. Regards, &rzej;
Andrzej Krzemienski wrote:
The std::variant also provides a second overload of `visit` that explicitly defines the return type: http://eel.is/c++draft/variant.visit
That's C++20. I haven't implemented the C++20 additions yet.
Andrzej Krzemienski wrote:
Consider the following example:
``` struct X {};
struct Guard { explicit Guard(int) {} Guard(Guard&&) = delete; };
variant
v {}; v.emplace<Guard>(1); ``` This works fine with std::variant, but fails to compile with variant2::variant, because the latter requires all the alternative types, at least in one implementaiton of the never-empty trick, to be move-constructible. But this requirement is never listed in either global requirements for alternative types stored in variant, or in the specification of functions `emplace()`.
The documentation is correct, the implementation is wrong here; this should work and the fact that it doesn't is a bug. Thanks.
niedz., 7 kwi 2019 o 05:33 Peter Dimov via Boost
Andrzej Krzemienski wrote:
Consider the following example:
``` struct X {};
struct Guard { explicit Guard(int) {} Guard(Guard&&) = delete; };
variant
v {}; v.emplace<Guard>(1); ``` This works fine with std::variant, but fails to compile with variant2::variant, because the latter requires all the alternative types, at least in one implementaiton of the never-empty trick, to be move-constructible. But this requirement is never listed in either global requirements for alternative types stored in variant, or in the specification of functions `emplace()`.
The documentation is correct, the implementation is wrong here; this should work and the fact that it doesn't is a bug. Thanks.
Might I ask how you plan to fix this bug? Go double-buffer in this case? Regards, &rzej;
Andrzej Krzemienski wrote: ...
Consider the following example:
``` struct X {};
struct Guard { explicit Guard(int) {} Guard(Guard&&) = delete; };
variant
v {}; v.emplace<Guard>(1); ``` ... The documentation is correct, the implementation is wrong here; this should work and the fact that it doesn't is a bug. Thanks. Might I ask how you plan to fix this bug?
It's fixed in https://github.com/pdimov/variant2/commit/9f7e525984ac87ccb90799c9bb2283946d....
Go double-buffer in this case?
There's no reason to double-buffer here, because X is nothrow default constructible, so it can be used as a fallback type.
Andrzej Krzemienski wrote:
Specification of function `emplace<I>()`: It says:
On exception: If the list of alternatives contains monostate, the contained value is either unchanged, or monostate{};
In what situation on exception is the contained value left unchanged? This question applies to the next bullet as well.
I prefer to leave this unspecified.
swap(): What does it throw and when? Other functions specify it but this one does not.
swap() is defined as equivalent to code sequences (depending on whether the indices match), so the effects, including exceptions thrown, are the effects of those code sequences.
get<I>(): Specification says,
If v.index() is I, returns a reference to the object stored in the variant. Otherwise, throws bad_variant_access.
This means that if I provide an index that is far greater than `sizeof...(T)`, the program should compile fine and throw an exception at run-time.
Not quite, because get<> returns variant_alternative_t>, and this expression causes a substitution failure when I >= sizeof...(T). So there's an implicit "Constraints: I < sizeof...(T)". Maybe it needs to be made explicit.
According to the specs, the following should work:
``` void f() {} variant2::visit(f); ``` which is not incorrect, but strange.
This is also the case with std::variant if I'm not mistaken? The purpose is probably to support variant<> parameter packs in a consistent way, even when the pack is empty.
niedz., 7 kwi 2019 o 14:10 Peter Dimov via Boost
Andrzej Krzemienski wrote:
According to the specs, the following should work:
``` void f() {} variant2::visit(f); ``` which is not incorrect, but strange.
This is also the case with std::variant if I'm not mistaken? The purpose is probably to support variant<> parameter packs in a consistent way, even when the pack is empty.
Yes, this is also the case with std::variant. Regards, &rzej;
participants (2)
-
Andrzej Krzemienski
-
Peter Dimov