C++ Logo

std-proposals

Advanced search

Re: [std-proposals] How should we tag a class? Use an empty base class or a typedef?

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Mon, 27 May 2024 19:48:33 -0400
On Mon, May 27, 2024 at 4:50 PM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:

> On Mon, May 27, 2024 at 4:04 PM Gašper Ažman wrote
> >
> > strongly against a baseclass. The reason anyone would use anything but
> > std::elide is because they have their own, and changing that is
> > difficult.
> >
> > Request a `bool std::is_elider_v<my-class>` specialization and then
> > have `template <typename X> concept elider =
> std::is_elider_v<remove_cvref_t<X>>`.
>
> I think that's more complicated than just having:
>
> class elide {
> public:
> typedef true_type tag_elide;
> };
>
> Plus, my strategy has the added versatility of being able to override
> it in a derived class with "false" instead of "true":
>
> class MyElide : public std::elide {
> public:
> typedef std::false_type tag_elide;
> };
>

I agree that Gašper's suggestion of an ad-hoc free-floating
`is_foo_v`/`is_foo` variable/class template is not optimal. C++20 Ranges
ended up with a soup of free-floating templates of that form — some
variable templates, some class templates — and it's just icky.
A member typedef is generally the way to go. However, in any
member-typedef-based design that's intended for a *widely reusable library*,
I strongly recommend the pattern
    struct MyT {
      using is_library3_thing = MyT; // as seen in libc++'s
__trivially_relocatable
    };
    template<class X> concept library3_thing = std::is_same_v<typename
X::is_library3_thing, X>;
rather than either of the state-of-the-art approaches
    struct MyT {
      using is_library1_thing = void; // as seen in C++14's is_transparent
      using is_library2_thing = true_type; // as seen in C++11's
propagate_on_container_copy_assignment
    };
    template<class X> concept library1_thing = requires { typename
X::is_library1_thing; };
    template<class X> concept library2_thing = X::is_library2_thing::value;
The reason is inheritance.
    struct MyU : public MyT {};
If `MyU` inherits from `MyT`, then it is_library1_thing by definition and
there's no way to change that.
It's also is_library2_thing by default, unless the author of `MyU` has the
presence of mind to define `using is_library2_thing = false_type;` in the
body of `MyU`.
But it's *not* is_library3_thing by default. You have to re-opt-in to
`is_library3_thing` at each level of inheritance (that is, at each leaf).
This is what you want for most traity things, and I think it's what you'd
want for your `elide` thingie, too. (Mind you, I don't think your `elide`
thingie makes enough sense to say anything really concrete about what it
"should" be like.)

–Arthur

Received on 2024-05-27 23:48:46