Date: Wed, 29 May 2024 17:03:49 +0100
Fair. I guess an initializer_t is always callable as an initialization
function all by itself. The elide class should be callable, too,
though, so it can compose with all the other eliders anyone else might
have.
On Wed, May 29, 2024 at 2:58 PM Arthur O'Dwyer
<arthur.j.odwyer_at_[hidden]> wrote:
>
> On Mon, May 27, 2024 at 7:55 PM Gašper Ažman <gasper.azman_at_[hidden]> wrote:
>>
>> Arthur,
>>
>> The whole reason to even have std::is_elide_v separate is because you can't necessarily change your own widget. Like, I have one called initializer_t, I want to use it as if it were std::elide, but I can't fix it to have the nested typedef, perhaps because I don't have write access.
>
>
> You do? My understanding of Frederick's `std::elide` idea was that it would be basically the same thing that I call "SCSE", i.e.:
> https://godbolt.org/z/T7W3GW35h
>
> template<class T, class F>
> struct elide {
> F lam_;
> explicit elide(F lam) : lam_(lam) {}
> operator T() const { return lam_(); }
> };
> elide(auto&& lam) -> elide<decltype(lam()), decltype(lam)>;
>
> template<class T>
> struct list {
> template<class... Args> void emplace_back(Args&&...);
> };
> std::list<Immovable> v;
> Immovable make_immovable();
> v.emplace_back(elide([]() { return make_immovable(); }));
>
> and then we have to postulate that there's an ImmovableAny like
> https://godbolt.org/z/nh7ehcK9o
> struct ImmovableAny {
> template<class T> requires (!std::is_same_v<std::decay_t<T>, ImmovableAny>)
> ImmovableAny(T&&);
> ImmovableAny(ImmovableAny&&) = delete;
> };
> that we want to emplace like this:
> std::list<ImmovableAny> v;
> ImmovableAny make_immovable();
> v.emplace_back(elide([]() { return make_immovable(); }));
> and then the problem we're trying to solve is that this will construct an `ImmovableAny` holding an `elide`, rather than calling `elide::operator ImmovableAny`. Frederick's proposed solution (which, as I said, IMO is very bad) is that template type deduction should never deduce a (possibly cvref-qualified) specialization of `elide`.
>
> So you have code that uses a third-party `initializer_t` that acts just like this `elide`? and since it's third-party you can't modify it, nor can you rewrite it to match the seven-liner above? but you do want that third-party `initializer_t` to share this template-type-deduction carveout in the core language, because you want to use types like `ImmovableAny`?
>
> I mean, I guess there's nothing logically inconsistent about any of that scenario. It just feels like we've gone two or three levels beyond realism at this point.
>
> ---
>
> In general, the type author should be in control of their type's behavior, and users shouldn't be allowed to ODR-violate that type directly. The accepted way to "modify" a type's behavior is to wrap it in a different type — with a different name for ODR purposes — and apply all your new behaviors to that new type.
> Two examples:
> - P1144 would have permitted taking a third-party type that you can't modify (like boost::static_vector) and — not directly warranting it (which would violate ODR for functions like `vector<static_vector>::erase`) — but wrapping it in your own rule-of-zero type which you can warrant. [P2786 won't allow this.]
> - In 2023, LWG finally confirmed that ordinary programmers aren't allowed to specialize `allocator_traits` for their own types. The right place to customize the behavior of an allocator `A` is inside `A` itself, not inside `allocator_traits<A>`. If you don't own `A`, then you must wrap it in `B` and customize `B` instead.
>
> my $.02,
> Arthur
function all by itself. The elide class should be callable, too,
though, so it can compose with all the other eliders anyone else might
have.
On Wed, May 29, 2024 at 2:58 PM Arthur O'Dwyer
<arthur.j.odwyer_at_[hidden]> wrote:
>
> On Mon, May 27, 2024 at 7:55 PM Gašper Ažman <gasper.azman_at_[hidden]> wrote:
>>
>> Arthur,
>>
>> The whole reason to even have std::is_elide_v separate is because you can't necessarily change your own widget. Like, I have one called initializer_t, I want to use it as if it were std::elide, but I can't fix it to have the nested typedef, perhaps because I don't have write access.
>
>
> You do? My understanding of Frederick's `std::elide` idea was that it would be basically the same thing that I call "SCSE", i.e.:
> https://godbolt.org/z/T7W3GW35h
>
> template<class T, class F>
> struct elide {
> F lam_;
> explicit elide(F lam) : lam_(lam) {}
> operator T() const { return lam_(); }
> };
> elide(auto&& lam) -> elide<decltype(lam()), decltype(lam)>;
>
> template<class T>
> struct list {
> template<class... Args> void emplace_back(Args&&...);
> };
> std::list<Immovable> v;
> Immovable make_immovable();
> v.emplace_back(elide([]() { return make_immovable(); }));
>
> and then we have to postulate that there's an ImmovableAny like
> https://godbolt.org/z/nh7ehcK9o
> struct ImmovableAny {
> template<class T> requires (!std::is_same_v<std::decay_t<T>, ImmovableAny>)
> ImmovableAny(T&&);
> ImmovableAny(ImmovableAny&&) = delete;
> };
> that we want to emplace like this:
> std::list<ImmovableAny> v;
> ImmovableAny make_immovable();
> v.emplace_back(elide([]() { return make_immovable(); }));
> and then the problem we're trying to solve is that this will construct an `ImmovableAny` holding an `elide`, rather than calling `elide::operator ImmovableAny`. Frederick's proposed solution (which, as I said, IMO is very bad) is that template type deduction should never deduce a (possibly cvref-qualified) specialization of `elide`.
>
> So you have code that uses a third-party `initializer_t` that acts just like this `elide`? and since it's third-party you can't modify it, nor can you rewrite it to match the seven-liner above? but you do want that third-party `initializer_t` to share this template-type-deduction carveout in the core language, because you want to use types like `ImmovableAny`?
>
> I mean, I guess there's nothing logically inconsistent about any of that scenario. It just feels like we've gone two or three levels beyond realism at this point.
>
> ---
>
> In general, the type author should be in control of their type's behavior, and users shouldn't be allowed to ODR-violate that type directly. The accepted way to "modify" a type's behavior is to wrap it in a different type — with a different name for ODR purposes — and apply all your new behaviors to that new type.
> Two examples:
> - P1144 would have permitted taking a third-party type that you can't modify (like boost::static_vector) and — not directly warranting it (which would violate ODR for functions like `vector<static_vector>::erase`) — but wrapping it in your own rule-of-zero type which you can warrant. [P2786 won't allow this.]
> - In 2023, LWG finally confirmed that ordinary programmers aren't allowed to specialize `allocator_traits` for their own types. The right place to customize the behavior of an allocator `A` is inside `A` itself, not inside `allocator_traits<A>`. If you don't own `A`, then you must wrap it in `B` and customize `B` instead.
>
> my $.02,
> Arthur
Received on 2024-05-29 16:04:02