|
| 1 | +<?xml version='1.0' encoding='utf-8' standalone='no'?> |
| 2 | +<!DOCTYPE issue SYSTEM "lwg-issue.dtd"> |
| 3 | + |
| 4 | +<issue num="4473" status="New"> |
| 5 | +<title>`sender_concept` tags should use `_tag`, not `_t`</title> |
| 6 | +<section><sref ref="[execution.syn]"/></section> |
| 7 | +<submitter>Arthur O'Dwyer</submitter> |
| 8 | +<date>12 Nov 2025</date> |
| 9 | +<priority>99</priority> |
| 10 | + |
| 11 | +<discussion> |
| 12 | +<p> |
| 13 | +In November 2025 it was observed that the STL has exactly two places where member typedefs |
| 14 | +are used to specify what concepts a type claims to conform to. The first is iterator concept |
| 15 | +tags, and the second is sender/receiver concept tags. The former match widespread Boost |
| 16 | +practice in using the suffix "`_tag`", e.g. |
| 17 | +</p> |
| 18 | +<blockquote><pre> |
| 19 | +struct forward_iterator_tag {}; |
| 20 | +using iterator_category = std::forward_iterator_tag; |
| 21 | +using iterator_concept = std::forward_iterator_tag; |
| 22 | +</pre></blockquote> |
| 23 | +<p> |
| 24 | +The latter, adopted for C++26, currently use the suffix "_t", e.g. |
| 25 | +</p> |
| 26 | +<blockquote><pre> |
| 27 | +struct sender_t {}; |
| 28 | +struct receiver_t {}; |
| 29 | +struct scheduler_t {}; |
| 30 | +struct operation_state_t {}; |
| 31 | +using sender_concept = sender_t; |
| 32 | +using receiver_concept = receiver_t; |
| 33 | +using scheduler_concept = scheduler_t; |
| 34 | +using operation_state_concept = operation_state_t; |
| 35 | +</pre></blockquote> |
| 36 | +<p> |
| 37 | +For consistency with the existing STL and with Boost, we should rename each of these four C++26 types: |
| 38 | +</p> |
| 39 | +<ul> |
| 40 | +<li><p>`sender_t` ==> `sender_tag`</p></li> |
| 41 | +<li><p>`receiver_t` ==> `receiver_tag`</p></li> |
| 42 | +<li><p>`scheduler_t` ==> `scheduler_tag`</p></li> |
| 43 | +<li><p>`operation_state_t` ==> `operation_state_tag`</p></li> |
| 44 | +</ul> |
| 45 | +<p> |
| 46 | +Note the distinction between member-typedef "concept tags" like `forward_iterator_tag` and |
| 47 | +`execution::sender_t(ag)`, as opposed to "tag constants" used in function signatures like |
| 48 | +`defer_lock_t` and `execution::get_allocator_t`. |
| 49 | +<p/> |
| 50 | +Concept tags: |
| 51 | +</p> |
| 52 | +<ul> |
| 53 | +<li><p>intended for use as member typedefs</p></li> |
| 54 | +<li><p>member typedef's name ends in `_category` or `_concept`</p></li> |
| 55 | +<li><p>tag type's name ends in `_tag`</p></li> |
| 56 | +<li><p>unsuffixed form is a concept, e.g. `concept forward_iterator`, `concept execution::sender`, `concept execution::operation_state`</p></li> |
| 57 | +<li><p>always a 1:1 relationship between a tag and a concept</p></li> |
| 58 | +<li><p>frequently used as a base class for more tag types</p></li> |
| 59 | +</ul> |
| 60 | +<p> |
| 61 | +Tag constants: |
| 62 | +</p> |
| 63 | +<ul> |
| 64 | +<li><p>never used as member typedefs</p></li> |
| 65 | +<li><p>tag type's name ends in `_t`</p></li> |
| 66 | +<li><p>unsuffixed form is a constant global variable, e.g. `constexpr auto allocator_arg`, `in_place_type`, |
| 67 | +`defer_lock`, `adopt_lock`, `execution::set_value`, `execution::get_allocator`</p></li> |
| 68 | +<li><p>never represent a concept</p></li> |
| 69 | +<li><p>never used as a base class for more tag types</p></li> |
| 70 | +</ul> |
| 71 | +<p> |
| 72 | +Examples of "concept tags" from outside the STL include these from Boost: |
| 73 | +</p> |
| 74 | +<ul> |
| 75 | +<li><p>Boost.Graph has member typedef `directed_category` corresponding to `directed_tag`, `undirected_tag`, etc.</p></li> |
| 76 | +<li><p>Boost.Graph has member typedef `traversal_category` corresponding to `vertex_list_graph_tag`, etc.</p></li> |
| 77 | +<li><p>Boost.Fusion has member typedef `fusion_tag` corresponding to `deque_tag`, `vector_tag`, etc.</p></li> |
| 78 | +<li><p>Boost.Numeric.Odeint has member typedef `stepper_category` corresponding to `stepper_tag`, etc.</p></li> |
| 79 | +<li><p>Boost.Numeric.Ublas has member typedef `dispatch_category` corresponding to `row_major_tag`, `column_major_tag`, etc.</p></li> |
| 80 | +<li><p>Boost.Numeric.Ublas has member typedef `type_category` corresponding to `tensor_tag`, etc.</p></li> |
| 81 | +<li><p>Boost.Numeric.Ublas has member typedef `storage_category` corresponding to `dense_tag`, `packed_tag`, |
| 82 | +`sparse_tag`, etc.</p></li> |
| 83 | +</ul> |
| 84 | +<p> |
| 85 | +A few more examples can be found via GitHub search: |
| 86 | +</p> |
| 87 | +<ul> |
| 88 | +<li><p><a href="https://github.com/search?q=language%3AC%2B%2B+%2F%28%3F-i%29using+.*_concept+%3D+.*_tag%3B%2F+NOT+%2Fitera%3Ftor_concept%2F&type=code">[search-results A]</a></p></li> |
| 89 | +<li><p><a href="https://github.com/search?q=language%3AC%2B%2B+%2F%28%3F-i%29using+.*_category+%3D+.*_tag%3B%2F+NOT+%2Fiterator_category%2F&type=code">[search-results B]</a></p></li> |
| 90 | +</ul> |
| 91 | +<p> |
| 92 | +(Notably Jared Hoberock's Croquet, a "prototype implementation of C++ executors, senders, & receivers" from ~2019, |
| 93 | +used `sender_tag` in place of `sender_t`.) |
| 94 | +<p/> |
| 95 | +For consistency with the existing library, we should rename each of these four C++26 types, e.g. `sender_t` to |
| 96 | +`sender_tag`... and this needs to be done now, in the C++26 cycle, because it cannot be done later (in which case |
| 97 | +we will have failed the community). |
| 98 | +</p> |
| 99 | +</discussion> |
| 100 | + |
| 101 | +<resolution> |
| 102 | +</resolution> |
| 103 | + |
| 104 | +</issue> |
0 commit comments