C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [execution] sender_t or sender_tag ?

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Mon, 10 Nov 2025 14:03:59 -0500
On Mon, Nov 10, 2025 at 1:46 PM Arthur O'Dwyer via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> On Mon, Nov 10, 2025 at 1:06 PM Jonathan Wakely <cxx_at_[hidden]> wrote:
>>
>> On Mon, 10 Nov 2025, 15:51 Arthur O'Dwyer via Std-Proposals, <std-proposals_at_[hidden]> wrote:
>>>
>>> On Mon, Nov 10, 2025 at 1:15 AM 叶易安 via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>>
>>>> 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.

Well, there's already been quite some drift as to what `_t` means.

Initially, derived from C, `_t` is used for some kind of
implementation-defined type that is exposed via a typedef. `int32_t`
is a typedef for some implementation-specific type. And C++ inherited
this meaning, applying it to `std::nullptr_t` and the like.

The concept tags always used `_tag`, since they're not like the above.
First and foremost, they're class types.

The problem is that someone decided that certain interface tags used
for overload resolution should use the `_t` suffix. They *should* have
used `_tag`, but they didn't.

So now we have this distinction between "tag" as "class type used in
by concepts to detect a property of a type that is otherwise
undetectable by a concept," and "tag" as "class type used to
disambiguate overload resolution". The issue is that I don't think
this distinction really matters to anybody. Yes, these are two
different uses of the "tag" concept, but I don't think someone really
needs to be able to look at a tag type's name and know whether it's
used for overload resolution or concept checking. You'll find out by
how it's used in code.

Unfortunately, that ship has already sailed, so we may as well
preserve the otherwise useless distinction.

Received on 2025-11-10 19:04:12