We notice that we have 2 available 'tags' in C++ standard library now:
=====
class my_iterator { using iterator_concept = std::random_access_iterator_tag };
class my_sender { using sender_concept = std::execution::sender_t; }
=====
Oops, the first ends with `_tag` and the second ends with `_t`. But they share the same effect (to make a class satisfy a particular concept). Should we unify them?
Hmmm, before that let's see where we use `_tag` and `_t` in C++ standard library.
xxx_t:
1. <type_traits>. When we have `add_const<T>::type`, we typedef an `add_const_t<T>`.
2. Typedef which unifies different platforms. `uint8_t`, `wchar_t`, `clock_t`, etc.
3. Utility type of global constexpr variable. e.g. nullopt_t, unexpected_t, etc.
4. Utility type of function overload parameters. e.g. unique_lock(defer_lock_t), vector(from_range_t), etc.
xxx_tag:
1. Customize xxx_concept in class. e.g. using iterator_concept forward/bidirectional/.../random_access_iterator_tag.
Should we rename `std::execution::sender_t` into `std::execution::sender_tag`?
Thank you!
Yes, it certainly seems so (at first glance) to me. Like you, I'm not aware of any prior art for using `_t` as the suffix for a tag type.
Aren't allocator_arg_t, inplace_t, defer_lock_t etc. tag types?
Not in the same sense. Those are the kind of tag type where we write
namespace std { struct foo_t {}; constexpr foo_t foo; }
usercode(std::foo, ...);
sender_t and receiver_t and foo_iterator_tag are the kind of tag type where we write
namespace std { struct foo_tag {}; }
struct Usercode { using foo_concept = std::foo_tag; };
We do indeed have the same kind of overloaded-English-word problem with "tag type" that we have with "traits class," but it's true that sender_t and receiver_t are the same kind as foo_iterator_tag and not at all the same kind as allocator_arg_t, inplace_t, inplace_type_t, defer_lock_t, adopt_lock_t, etc. Nor are they the same kind as std::nullopt_t, nor std::monostate.
Using a "tag" suffix is unique to the iterator categories, which are from 30 years ago and that design hasn't been repeated elsewhere in the library.
But that's precisely because no other place in the library has repeated the
using foo_category = ...;
using foo_concept = ...;
design, before now, isn't it? AFAIR the closest we've ever come between C++98's iterator concept tags and C++26 S/R concept tags is the (IMO ill-advised)
using is_transparent = void;
which didn't introduce any new tag types.
–Arthur