C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Tagging

From: Jeremy Rifkin <jeremy_at_[hidden]>
Date: Sat, 19 Apr 2025 18:10:21 -0500
Hi,
This is just a worse version of Rust's traits. While I wish C++ had
something like traits, that ship has sailed. Duck typing is the way of C++.

Cheers,
Jeremy

On Apr 19 2025, at 5:58 pm, Frederick Virchanza Gotham via Std-Proposals
<std-proposals_at_[hidden]> wrote:

> This past week in the thread about "std::arithmetic", we've been
> talking about different ways of tagging a class.
>
> Here's a class that has a tag that will be inherited by all derived classes:
>
> #include <type_traits>
>
> class MyClass {
> public:
> typedef int tag_arithmetic;
> };
>
> class MyDerivedClass : public MyClass {
>
> };
>
> template<typename T>
> void Func(void)
> {
> static_assert( std::is_same_v<int, typename T::tag_arithmetic> );
> }
>
> int main(void)
> {
> Func<MyDerivedClass>();
> }
>
> The derived class can opt out of the tag as follows:
>
> class MyDerivedClass : public MyClass {
> public:
> typedef void tag_arithmetic;
> };
>
> And here's a way of writing a class with a tag which is not implicitly
> inherited by derived classes:
>
> class MyClass {
> public:
> typedef MyClass tag_arithmetic;
> };
>
> class MyDerivedClass : public MyClass {
>
> };
>
> template<typename T>
> void Func(void)
> {
> static_assert( std::is_same_v<T, typename T::tag_arithmetic> );
> }
>
> int main(void)
> {
> Func<MyDerivedClass>();
> }
>
> And the derived class can opt in as follows:
>
> class MyDerivedClass : public MyClass {
> public:
> typedef MyDerivedClass tag_arithmetic;
> };
>
> An alternative to using 'typedef' for creating tags is to use a static
> member boolean as follows:
>
> #include <type_traits>
>
> class MyClass {
> public:
> static constexpr bool tag_arithmetic = true;
> };
>
> class MyDerivedClass : public MyClass {
>
> };
>
> template<typename T>
> void Func(void)
> {
> static_assert( T::tag_arithmetic );
> }
>
> int main(void)
> {
> Func<MyDerivedClass>();
> }
>
> And the derived class can opt out as follows:
>
> class MyDerivedClass : public MyClass {
> public:
> static constexpr bool tag_arithmetic = false;
> };
>
> And another way of tagging a class is to use an empty base class as follows:
>
> class tag_arithmetic {};
>
> class MyClass : public tag_arithmetic {};
>
> class MyDerivedClass : public MyClass {};
>
> template<typename T>
> void Func(void)
> {
> static_assert( std::is_base_of_v<tag_arithmetic, T> );
> }
>
> int main(void)
> {
> Func<MyDerivedClass>();
> }
>
> (This particular method could get really interesting at runtime with
> 'dynamic_cast')
>
> In all the ways shown so far of tagging a class, the tag is part of
> the class definition. What this means is that if a class is inside a
> header file, and if you want to add a tag to that class, then you must
> edit the header file. There is another method of tagging though that
> allows you to leave the class definition intact, as follows:
>
> template<typename T>
> constexpr bool tag_arithmetic = false;
>
> class MyClass {};
>
> template<>
> constexpr bool tag_arithmetic<MyClass> = true;
>
> class MyDerivedClass : public MyClass {};
>
> template<>
> constexpr bool tag_arithmetic<MyDerivedClass> = true;
>
> template<typename T>
> void Func(void)
> {
> static_assert( tag_arithmetic<T> );
> }
>
> int main(void)
> {
> Func<MyDerivedClass>();
> }
>
> As you can see in the above code snippet, the tag is not automatically
> inherited by derived classes.
>
> I want to discuss the possibility of standardising tagging. I think
> any given tag should have the following configurable attributes:
> 1) Tag is inherited by default, or not inherited by default
> 2) Tag is within class definition, or outside class definition
>
> So here's a tag that's automatically inherited by derived classes:
>
> class MyClass {
> public:
> _Tag arithmetic;
> };
>
> You can consider the above class definition to be short-hand for:
>
> class MyClass {
> public:
> _Tag arithmetic = true;
> };
>
> Here's a tag that's not automatically inherited by derived classes:
>
> class MyClass {
> public:
> _Tag arithmetic : final;
> };
>
> which again is short-hand for:
>
> class MyClass {
> public:
> _Tag arithmetic : final = true;
> };
>
> Here is how you write a derived class and tell the compiler "This
> class has the tag if any of its bases have the tag" -- so that you can
> opt into a tag which is marked 'final' in the base class as follows:
>
> class MyClass {
> public:
> _Tag arithmetic : final;
> };
>
> class MyDerivedClass : public MyClass {
> public:
> _Tag arithmetic = ?;
> };
>
> And if you don't want to edit the header file for a class, then you
> can add the tag after the class definition as follows:
>
> class MyClass {};
> MyClass << _Tag arithmetic : final;
> class MyDerivedClass : public MyClass {};
> MyDerivedClass << _Tag arithmetic = ?;
>
> And here's how a derived class opts out of a base class's tag:
>
> class MyClass {
> public:
> _Tag arithmetic;
> };
>
> class MyDerivedClass : public MyClass {
> public:
> _Tag arithmetic = false;
> };
>
> Note that the expression after the '=' operator can be any constant
> expression that evaluates to a boolean, for example:
>
> class MyClass {
> public:
> _Tag arithmetic = is_debugging_enabled && is_built_as_static_executable;
> };
>
> Now a few of you might be thinking right now:
> So you've shown us 5 different ways of tagging a class, so what's
> good about increasing that number to 6? Well here's what I'm thinking:
>
> Point No. 1: If the 6th tagging system is clearly superior to
> the other 5, then people will adopt it and abandon the other 5.
> Point No. 2: Use of the new keyword "_Tag" makes it more
> explicit and easier to figure out exactly what the code is doing at a
> glance.
> Point No. 3: Once tagging is standardised, the documentation
> for an SDK library can have a comment such as "The type T must have
> the arithmetic tag", and the programmer doesn't have to scratch their
> head wondering which of the 5 tagging systems is used -- instead they
> know it's the standardised system.
>
> And finally: The reflection system would allow constexpr iteration
> through a class's tags as follows:
>
> for ( auto const &t = std::get_tags( typeid(T) ) )
> {
> cout << "Tag: " << t.name() << " Inherited from base: " <<
> (t.was_inherited() ? "yes" : "no") << endl;
> }
>
> FIN.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2025-04-19 23:10:24