Looks good. Covers most of the use cases.

Please consider adding filesystem::path, pair, tuple, string_view?,
error_code, list, deque (myabe all the other containers), optional, variant
itself (for cases when variant holds another variant).

It would be very useful for the _Never_valueless_alt to return true for
aggregates that hold only never valueles types. Not sure that there is a
compiler builtin for getting all the aggregates types, but it could be
implemented via metaprogramming.

When is the ABI freeze for the C++17 additions?


On Wed, Apr 3, 2019, 19:25 Jonathan Wakely <jwak...@redhat.com> wrote:

> Avoid creating arbitrarily large objects on the stack when creating
> temporaries in order to provide the strong exception-safety guarantee.
>
> Also implement Antony Polukhin's suggestion to whitelist specific types
> that can be efficiently move-assigned, so that emplacing those types
> never causes a variant to become valueless. The whitelisted types are:
> all trivially copyable types no greater than 256 bytes in size,
> std::shared_ptr, std::weak_ptr, std::unique_ptr, std::function, and
> std::any. Additionally, std::basic_string, std::vector, and
> __gnu_debug::vector are whitelisted if their allocator traits give them
> a non-throwing move assignment operator. Specifically, this means
> std::string is whitelisted, but std::pmr::string is not.
>
>         PR libstdc++/87431 (again)
>         * include/bits/basic_string.h (__variant::_Never_valueless_alt):
>         Define partial specialization for basic_string.
>         * include/bits/shared_ptr.h (_Never_valueless_alt): Likewise for
>         shared_ptr and weak_ptr.
>         * include/bits/std_function.h (_Never_valueless_alt): Likewise for
>         function.
>         * include/bits/stl_vector.h (_Never_valueless_alt): Likewise for
>         vector.
>         * include/bits/unique_ptr.h (_Never_valueless_alt): Likewise for
>         unique_ptr.
>         * include/debug/vector (_Never_valueless_alt): Likewise for debug
>         vector.
>         * include/std/any (_Never_valueless_alt): Define explicit
>         specialization for any.
>         * include/std/variant (_Never_valueless_alt): Define primary
> template.
>         (__never_valueless): Use _Never_valueless_alt instead of
>         is_trivially_copyable.
>         (variant::emplace<N>(Args&&...)): Add special case for non-throwing
>         initializations to avoid try-catch overhead. Add special case for
>         scalars produced by potentially-throwing conversions. Use
>         _Never_valueless_alt instead of is_trivially_copyable for the
>         remaining strong exception-safety cases.
>         (variant::emplace<N>(initializer_list<U>, Args&&...)): Likewise.
>         * testsuite/20_util/variant/87431.cc: Run both test functions.
>         * testsuite/20_util/variant/exception_safety.cc: New test.
>         * testsuite/20_util/variant/run.cc: Use pmr::string instead of
> string,
>         so the variant becomes valueless.
>
> I'd like to commit this to trunk this week. Does anybody see any
> problems with this approach?
>
> As the comment in <variant> says, the _Never_valueless_alt trait has
> ABI impact, so we should define it for more types now, or not at all.
> When we add new types to the library we should consider if they need a
> specialization of _Never_valueless_alt.
>
> Should we also whitelist some (all?) other containers, as they all
> have conditionally noexcept move assignment operators, and those
> assignments are efficient when noexcept (because they don't
> reallocate). Are there other types in the library we should consider
> it for?
>
> std::optional<T> is automatically whitelisted for trivially copyable
> types, because if T is trivially copyable then optional<T> should be
> too. We could also whitelist other specializations though:
>
> template<typename T>
> struct _Never_valueless_alt<optional<T>>
> : _Never_valueless_alt<T>
> { };
>
> We could also do:
>
> template<typename T, typename U>
> struct _Never_valueless_alt<pair<T, U>>
> : __and_<_Never_valueless_alt<T>, _Never_valueless_alt<U>>
> { };
>
> template<typename... T>
> struct _Never_valueless_alt<tuple<T...>>
> : __and_<_Never_valueless_alt<T>...>
> { };
>
> In case it isn't clear, under normal circumstances something like a
> variant<pair<int,int>,int> won't ever become valueless anyway, because
> initialization of those alternatives doesn't throw. But what can throw
> is a conversion operator that is invoked to produce one of those
> alternatives. The _Never_valueless_alt trait says that emplacing an
> alternative of that type should do some extra work to ensure the
> variant doesn't become valueless *ever*. And then a variant that only
> has never-valueless alternatives is itself never-valueless, and so the
> valueless_by_exception() member just returns false, and visitation is
> more efficient as it doesn't need to deal with the valueless state.
>
>

Reply via email to