Doc. no. | D????
Date: | 2026-06-15
Project: | Programming Language C++
Reply to: | Jonathan Wakely <lwgchair@gmail.com>

# C++ Standard Library Active Issues List (Revision R126)

Revised 2026-06-15 at 14:46:45 UTC

Reference ISO/IEC IS 14882:2024(E)

Also see:

- Table of Contents for all library issues.
- Index by Section for all library issues.
- Index by Status for all library issues.
- Library Defect Reports and Accepted Issues
- Library Closed Issues List

The purpose of this document is to record the status of issues which have come before the Library Working Group (LWG) of the INCITS PL22.16 and ISO WG21 C++ Standards Committee. Issues represent potential defects in the ISO/IEC IS 14882:2024(E) document.

This document contains only library issues which are actively being considered by the Library Working Group, i.e., issues which have a status of New, Open, Ready, or Review. See Library Defect Reports and Accepted Issues for issues considered defects and Library Closed Issues List for issues considered closed.

The issues in these lists are not necessarily formal ISO Defect Reports (DR's). While some issues will eventually be elevated to official Defect Report status, other issues will be disposed of in other ways. See Issue Status.

Prior to Revision 14, library issues lists existed in two slightly different versions; a Committee Version and a Public Version. Beginning with Revision 14 the two versions were combined into a single version.

This document includes [bracketed italicized notes] as a reminder to the LWG of current progress on issues. Such notes are strictly unofficial and should be read with caution as they may be incomplete or incorrect. Be aware that LWG support for a particular resolution can quickly change if new viewpoints or killer examples are presented in subsequent discussions.

For the most current official version of this document see http://www.open-std.org/jtc1/sc22/wg21/.

Public information as to how to obtain a copy of the C++ Standard, join the standards committee, submit an issue, or comment on an issue can be found at isocpp.org.

How to submit an issue

1. Mail your issue to the author of this list.
2. Specify a short descriptive title. If you fail to do so, the subject line of your mail will be used as the issue title.
3. If the "From" on your email is not the name you wish to appear as issue submitter, then specify issue submitter.
4. Provide a brief discussion of the problem you wish to correct. Refer to the latest working draft or standard using [section.tag] and paragraph numbers where appropriate.
5. Provide proposed wording. This should indicate exactly how you want the standard to be changed. General solution statements belong in the discussion area. This area contains very clear and specific directions on how to modify the current draft. If you are not sure how to word a solution, you may omit this part. But your chances of a successful issue greatly increase if you attempt wording. If you know that the proposed change (or something close to it) has been implemented, please provide that information.
6. It is not necessary for you to use html markup. However, if you want to, you can <ins>insert text like this</ins> and <del>delete text like this</del>. The only strict requirement is to communicate clearly to the list maintainer exactly how you want your issue to look.
7. It is not necessary for you to specify other html font/formatting mark-up, but if you do the list maintainer will attempt to respect your formatting wishes (as described by html markup, or other common idioms).
8. It is not necessary for you to specify open date or last modified date (the date of your mail will be used).
9. It is not necessary for you to cross reference other issues, but you can if you like. You do not need to form the hyperlinks when you do, the list maintainer will take care of that.
10. One issue per email is best.
11. Between the time you submit the issue, and the next mailing deadline (date at the top of the Revision History), you own this issue. You control the content, the stuff that is right, the stuff that is wrong, the format, the misspellings, etc. You can even make the issue disappear if you want. Just let the list maintainer know how you want it to look, and he will try his best to accommodate you. After the issue appears in an official mailing, you no longer enjoy exclusive ownership of it.

## Revision History

- R126: 2026-06-15 Post-Brno 2026
  - Summary:
    - 561 open issues, down by 167.
    - 3506 closed issues, up by 334.
    - 51 reassigned issues, down by 6.
    - 4118 issues total, up by 161.
  - Details:
    - Added the following 5 Ready issues: 4547, 4562, 4577, 4587, 4588.
    - Added the following 3 Tentatively NAD issues: 4475, 4494, 4542.
    - Added the following 39 New issues: 4458, 4473, 4479, 4488, 4489, 4502, 4505, 4508, 4516, 4524, 4538, 4539, 4543, 4545, 4546, 4551, 4559, 4561, 4564, 4565, 4566, 4569, 4571, 4573, 4574, 4576, 4580, 4581, 4582, 4584, 4585, 4586, 4590, 4591, 4592, 4593, 4594, 4595, 4596.
    - Added the following 8 LEWG issues: 4503, 4509, 4530, 4558, 4560, 4563, 4572, 4589.
    - Added the following 2 Core issues: 4507, 4578.
    - Added the following 3 SG1 issues: 4519, 4570, 4575.
    - Added the following SG16 issue: 4541.
    - Added the following 8 WP issues: 4471, 4487, 4490, 4521, 4531, 4567, 4568, 4579.
    - Added the following 76 C++26 issues: 4438, 4439, 4440, 4441, 4442, 4443, 4444, 4445, 4446, 4447, 4448, 4449, 4450, 4451, 4452, 4453, 4454, 4455, 4456, 4457, 4459, 4460, 4461, 4462, 4463, 4464, 4465, 4467, 4468, 4469, 4472, 4474, 4476, 4477, 4478, 4480, 4481, 4483, 4485, 4486, 4491, 4492, 4493, 4496, 4497, 4499, 4500, 4504, 4506, 4510, 4511, 4512, 4514, 4517, 4522, 4523, 4525, 4527, 4528, 4529, 4532, 4533, 4535, 4536, 4537, 4540, 4544, 4548, 4549, 4550, 4552, 4553, 4554, 4555, 4556, 4557.
    - Added the following 10 Resolved issues: 4436, 4437, 4466, 4470, 4501, 4513, 4518, 4520, 4526, 4534.
    - Added the following 6 NAD issues: 4482, 4484, 4495, 4498, 4515, 4583.
    - Changed the following 20 issues to Ready (from New): 2592, 2595, 2713, 2714, 2811, 2819, 2983, 3053, 3126, 3172, 3353, 3584, 3647, 3696, 4022, 4039, 4055, 4367, 4397, 4406.
    - Changed the following 3 issues to Ready (from Open): 2136, 2137, 2220.
    - Changed the following issue to Tentatively NAD (from New): 4417.
    - Changed the following issue to Open (from New): 4356.
    - Changed the following issue to Core (from New): 2237.
    - Changed the following issue to SG6 (from New): 3402.
    - Changed the following 9 issues to WP (from New): 4121, 4125, 4158, 4182, 4218, 4249, 4267, 4359, 4385.
    - Changed the following issue to WP (from SG1): 3417.
    - Changed the following 13 issues to WP (from C++23): 2697, 2779, 2960, 3002, 3010, 3020, 3071, 3136, 3411, 3413, 3414, 3443, 3771.
    - Changed the following 4 issues to C++26 (from Ready): 4137, 4243, 4315, 4423.
    - Changed the following 57 issues to C++26 (from Tentatively Ready): 2991, 3090, 3627, 4020, 4136, 4166, 4253, 4255, 4256, 4257, 4265, 4266, 4269, 4274, 4275, 4276, 4280, 4286, 4291, 4292, 4293, 4294, 4297, 4299, 4300, 4301, 4305, 4312, 4317, 4318, 4328, 4340, 4341, 4342, 4343, 4345, 4346, 4349, 4351, 4366, 4370, 4372, 4375, 4377, 4382, 4384, 4398, 4399, 4403, 4407, 4412, 4413, 4415, 4416, 4422, 4425, 4426.
    - Changed the following 46 issues to C++26 (from New): 2746, 3504, 3599, 3662, 3797, 3831, 3891, 4026, 4122, 4133, 4143, 4151, 4223, 4230, 4251, 4259, 4260, 4272, 4290, 4304, 4308, 4314, 4316, 4324, 4325, 4339, 4347, 4358, 4360, 4369, 4376, 4378, 4379, 4383, 4388, 4420, 4424, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435.
    - Changed the following 7 issues to C++26 (from Open): 2414, 3343, 3454, 3777, 4015, 4090, 4130.
    - Changed the following 3 issues to C++26 (from LEWG): 4302, 4361, 4396.
    - Changed the following issue to C++26 (from SG1): 4354.
    - Changed the following 140 issues to C++26 (from WP): 2392, 2994, 3203, 3216, 3305, 3431, 3436, 3578, 3749, 3767, 3809, 3884, 3885, 3886, 3887, 3892, 3893, 3894, 3897, 3899, 3900, 3903, 3904, 3905, 3912, 3914, 3915, 3918, 3919, 3925, 3927, 3935, 3938, 3940, 3944, 3946, 3947, 3948, 3949, 3950, 3951, 3953, 3956, 3957, 3965, 3970, 3973, 3974, 3975, 3984, 3987, 3990, 4001, 4011, 4012, 4013, 4014, 4016, 4023, 4024, 4025, 4027, 4030, 4031, 4035, 4036, 4037, 4038, 4043, 4044, 4045, 4053, 4054, 4060, 4061, 4064, 4071, 4072, 4074, 4076, 4079, 4082, 4083, 4084, 4085, 4088, 4096, 4098, 4105, 4106, 4112, 4113, 4119, 4124, 4126, 4134, 4135, 4140, 4141, 4142, 4144, 4147, 4148, 4153, 4154, 4157, 4164, 4169, 4170, 4172, 4175, 4179, 4186, 4188, 4189, 4191, 4196, 4198, 4200, 4201, 4202, 4203, 4204, 4205, 4208, 4209, 4217, 4222, 4224, 4227, 4231, 4232, 4233, 4234, 4235, 4236, 4239, 4242, 4245, 4247.
    - Changed the following 35 issues to Resolved (from New): 2478, 2479, 2480, 2481, 2507, 3095, 3109, 3192, 3210, 3217, 3537, 3613, 3624, 3638, 3653, 4000, 4109, 4131, 4146, 4187, 4190, 4213, 4215, 4238, 4248, 4264, 4278, 4282, 4283, 4344, 4368, 4381, 4393, 4414, 4419.
    - Changed the following 2 issues to Resolved (from Open): 3625, 4387.
    - Changed the following 11 issues to Resolved (from LEWG): 936, 961, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4390.
    - Changed the following 2 issues to Resolved (from Pending NAD): 2302, 2382.
    - Changed the following issue to NAD Editorial (from Pending NAD Editorial): 2125.
    - Changed the following 30 issues to NAD (from Tentatively NAD): 3908, 3909, 3958, 3980, 3981, 3982, 3992, 4003, 4006, 4009, 4050, 4095, 4163, 4171, 4184, 4194, 4219, 4228, 4229, 4244, 4246, 4271, 4309, 4321, 4337, 4362, 4363, 4371, 4391, 4404.
    - Changed the following 18 issues to NAD (from New): 2248, 2472, 2737, 2858, 3162, 3220, 3268, 3484, 3615, 3641, 3686, 3722, 3845, 3952, 3979, 4220, 4221, 4402.
    - Changed the following 2 issues to NAD (from Open): 3161, 3840.
    - Changed the following 4 issues to NAD (from LEWG): 1484, 1488, 1493, 2884.
    - Changed the following 2 issues to NAD (from SG1): 2236, 4075.
    - Changed the following issue to NAD (from Deferred): 2202.
    - Changed the following issue to Dup (from New): 4195.
- R125: 2025-10-30 Pre-Kona 2025
  - Summary:
    - 728 open issues, up by 298.
    - 3172 closed issues, up by 172.
    - 57 reassigned issues, up by 21.
    - 3957 issues total, up by 491.
  - Details:
    - Added the following 4 Ready issues: 4137, 4243, 4315, 4423.
    - Added the following 54 Tentatively Ready issues: 4020, 4136, 4166, 4253, 4255, 4256, 4257, 4265, 4266, 4269, 4274, 4275, 4276, 4280, 4286, 4291, 4292, 4293, 4294, 4297, 4299, 4300, 4301, 4305, 4312, 4317, 4318, 4328, 4340, 4341, 4342, 4343, 4345, 4346, 4349, 4351, 4366, 4370, 4372, 4375, 4377, 4382, 4384, 4398, 4399, 4403, 4407, 4412, 4413, 4415, 4416, 4422, 4425, 4426.
    - Added the following 28 Tentatively NAD issues: 3958, 3980, 3981, 3982, 3992, 4003, 4006, 4009, 4050, 4095, 4163, 4171, 4184, 4194, 4219, 4228, 4229, 4244, 4246, 4271, 4309, 4321, 4337, 4362, 4363, 4371, 4391, 4404.
    - Added the following 249 New issues: 3945, 3952, 3954, 3955, 3959, 3960, 3961, 3962, 3963, 3964, 3966, 3967, 3968, 3969, 3972, 3976, 3977, 3979, 3983, 3985, 3986, 3989, 3991, 3993, 3994, 3995, 3997, 3998, 3999, 4000, 4002, 4005, 4007, 4008, 4010, 4017, 4018, 4021, 4022, 4026, 4028, 4029, 4032, 4033, 4034, 4039, 4040, 4041, 4046, 4047, 4048, 4049, 4051, 4052, 4055, 4057, 4059, 4062, 4063, 4065, 4066, 4067, 4068, 4073, 4077, 4078, 4080, 4081, 4089, 4091, 4092, 4093, 4094, 4099, 4100, 4101, 4102, 4103, 4104, 4107, 4109, 4110, 4111, 4114, 4115, 4116, 4117, 4118, 4120, 4121, 4122, 4123, 4125, 4127, 4128, 4129, 4131, 4132, 4133, 4138, 4139, 4143, 4145, 4146, 4151, 4152, 4155, 4158, 4159, 4160, 4161, 4162, 4165, 4167, 4168, 4173, 4176, 4180, 4181, 4182, 4183, 4185, 4187, 4190, 4192, 4193, 4195, 4197, 4199, 4206, 4207, 4210, 4211, 4212, 4213, 4214, 4215, 4216, 4218, 4220, 4221, 4223, 4225, 4226, 4230, 4237, 4238, 4240, 4248, 4249, 4251, 4252, 4254, 4258, 4259, 4260, 4261, 4262, 4263, 4264, 4267, 4268, 4270, 4272, 4273, 4277, 4278, 4279, 4281, 4282, 4283, 4285, 4287, 4288, 4290, 4295, 4296, 4298, 4303, 4304, 4306, 4307, 4308, 4311, 4313, 4314, 4316, 4319, 4320, 4322, 4323, 4324, 4325, 4326, 4327, 4339, 4344, 4347, 4352, 4353, 4355, 4356, 4357, 4358, 4359, 4360, 4364, 4367, 4368, 4369, 4373, 4374, 4376, 4378, 4379, 4380, 4381, 4383, 4385, 4386, 4388, 4392, 4393, 4394, 4395, 4397, 4400, 4402, 4405, 4406, 4409, 4410, 4411, 4414, 4417, 4418, 4419, 4420, 4421, 4424, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435.
    - Added the following 6 Open issues: 3988, 4015, 4070, 4090, 4130, 4387.
    - Added the following 24 LEWG issues: 4058, 4097, 4241, 4250, 4284, 4289, 4302, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4338, 4348, 4350, 4361, 4365, 4390, 4396, 4401, 4408.
    - Added the following Core issue: 4310.
    - Added the following 5 SG1 issues: 4004, 4075, 4174, 4177, 4354.
    - Added the following 3 SG9 issues: 4019, 4108, 4389.
    - Added the following 2 SG16 issues: 4087, 4156.
    - Added the following 105 WP issues: 3946, 3947, 3948, 3949, 3950, 3951, 3953, 3956, 3957, 3965, 3970, 3973, 3974, 3975, 3984, 3987, 3990, 4001, 4011, 4012, 4013, 4014, 4016, 4023, 4024, 4025, 4027, 4030, 4031, 4035, 4036, 4037, 4038, 4043, 4044, 4045, 4053, 4054, 4060, 4061, 4064, 4071, 4072, 4074, 4076, 4079, 4082, 4083, 4084, 4085, 4088, 4096, 4098, 4105, 4106, 4112, 4113, 4119, 4124, 4126, 4134, 4135, 4140, 4141, 4142, 4144, 4147, 4148, 4153, 4154, 4157, 4164, 4169, 4170, 4172, 4175, 4179, 4186, 4188, 4189, 4191, 4196, 4198, 4200, 4201, 4202, 4203, 4204, 4205, 4208, 4209, 4217, 4222, 4224, 4227, 4231, 4232, 4233, 4234, 4235, 4236, 4239, 4242, 4245, 4247.
    - Added the following 5 Resolved issues: 3978, 4042, 4069, 4149, 4150.
    - Added the following NAD Editorial issue: 4178.
    - Added the following 4 NAD issues: 3971, 3996, 4056, 4086.
    - Changed the following 2 issues to Tentatively Ready (from New): 3090, 3627.
    - Changed the following issue to Tentatively Ready (from LEWG): 2991.
    - Changed the following 4 issues to Open (from New): 2939, 3099, 3343, 3625.
    - Changed the following issue to Open (from LEWG): 3454.
    - Changed the following 2 issues to Open (from SG1): 2533, 3941.
    - Changed the following 17 issues to WP (from Tentatively Ready): 2994, 3884, 3885, 3887, 3893, 3894, 3903, 3904, 3905, 3912, 3914, 3915, 3925, 3927, 3935, 3938, 3940.
    - Changed the following 14 issues to WP (from New): 2392, 3203, 3216, 3431, 3436, 3578, 3749, 3809, 3886, 3892, 3897, 3899, 3900, 3919.
    - Changed the following 2 issues to WP (from Open): 3305, 3767.
    - Changed the following issue to WP (from LEWG): 3918.
    - Changed the following issue to WP (from SG16): 3944.
    - Changed the following 313 issues to C++23 (from WP): 2191, 2195, 2295, 2381, 2697, 2731, 2743, 2762, 2774, 2779, 2818, 2820, 2839, 2960, 2997, 3002, 3010, 3020, 3028, 3032, 3036, 3071, 3085, 3088, 3117, 3118, 3120, 3121, 3123, 3136, 3143, 3146, 3152, 3170, 3171, 3177, 3195, 3204, 3211, 3236, 3249, 3265, 3293, 3306, 3361, 3391, 3392, 3403, 3404, 3405, 3406, 3407, 3410, 3411, 3413, 3414, 3419, 3420, 3421, 3422, 3425, 3426, 3427, 3428, 3430, 3432, 3433, 3434, 3435, 3437, 3441, 3443, 3446, 3447, 3448, 3449, 3450, 3453, 3455, 3460, 3461, 3462, 3464, 3465, 3466, 3467, 3470, 3471, 3472, 3473, 3474, 3476, 3477, 3480, 3481, 3482, 3483, 3490, 3492, 3494, 3495, 3498, 3500, 3502, 3505, 3506, 3515, 3517, 3518, 3519, 3520, 3521, 3522, 3523, 3525, 3526, 3527, 3528, 3529, 3530, 3532, 3533, 3535, 3536, 3539, 3540, 3541, 3542, 3543, 3544, 3545, 3546, 3548, 3549, 3551, 3552, 3553, 3554, 3555, 3557, 3559, 3560, 3561, 3563, 3564, 3566, 3567, 3568, 3569, 3570, 3571, 3572, 3573, 3574, 3580, 3581, 3585, 3589, 3590, 3591, 3592, 3593, 3594, 3595, 3597, 3598, 3600, 3601, 3607, 3610, 3612, 3616, 3617, 3618, 3619, 3621, 3622, 3629, 3631, 3632, 3636, 3643, 3645, 3646, 3648, 3650, 3654, 3655, 3656, 3657, 3659, 3660, 3661, 3664, 3670, 3671, 3672, 3677, 3683, 3687, 3692, 3701, 3702, 3703, 3704, 3705, 3707, 3708, 3709, 3710, 3711, 3712, 3713, 3715, 3717, 3719, 3720, 3721, 3723, 3724, 3732, 3733, 3734, 3736, 3737, 3738, 3742, 3743, 3745, 3746, 3747, 3750, 3751, 3753, 3754, 3755, 3756, 3757, 3759, 3760, 3761, 3762, 3764, 3765, 3766, 3769, 3770, 3771, 3772, 3773, 3774, 3775, 3778, 3781, 3782, 3784, 3785, 3786, 3788, 3790, 3792, 3795, 3796, 3798, 3801, 3803, 3807, 3810, 3811, 3814, 3816, 3817, 3818, 3819, 3820, 3821, 3822, 3823, 3824, 3825, 3826, 3827, 3828, 3833, 3834, 3836, 3839, 3841, 3842, 3843, 3847, 3848, 3849, 3850, 3851, 3853, 3857, 3860, 3862, 3865, 3866, 3867, 3869, 3870, 3871, 3872, 3875, 3876, 3877, 3878, 3879, 3880, 3881.
    - Changed the following 4 issues to Resolved (from New): 2933, 3064, 3508, 3861.
    - Changed the following 3 issues to Resolved (from Open): 2088, 2524, 3003.
    - Changed the following 3 issues to Resolved (from LEWG): 2095, 2922, 2985.
    - Changed the following 6 issues to NAD (from Tentatively NAD): 2457, 3635, 3714, 3901, 3930, 3936.
    - Changed the following issue to NAD (from New): 2413.
    - Changed the following 3 issues to NAD (from LEWG): 532, 2885, 3776.
    - Changed the following issue to NAD (from SG1): 3485.
    - Changed the following issue to NAD (from SG9): 3913.
- R124: 2023-06-09 Pre-Varna
  - Summary:
    - 430 open issues, up by 25.
    - 3000 closed issues, up by 92.
    - 36 reassigned issues, down by 4.
    - 3466 issues total, up by 113.
  - Details:
    - Added the following 16 Tentatively Ready issues: 3884, 3885, 3887, 3893, 3894, 3903, 3904, 3905, 3912, 3914, 3915, 3925, 3927, 3935, 3938, 3940.
    - Added the following 5 Tentatively NAD issues: 3901, 3908, 3909, 3930, 3936.
    - Added the following 52 New issues: 3832, 3835, 3837, 3838, 3845, 3846, 3852, 3854, 3855, 3856, 3861, 3863, 3864, 3873, 3882, 3883, 3886, 3888, 3889, 3890, 3891, 3892, 3895, 3896, 3897, 3898, 3899, 3900, 3902, 3906, 3907, 3910, 3911, 3916, 3917, 3919, 3920, 3921, 3922, 3923, 3924, 3926, 3928, 3929, 3931, 3932, 3933, 3934, 3937, 3939, 3942, 3943.
    - Added the following 2 Open issues: 3840, 3844.
    - Added the following 2 LEWG issues: 3868, 3918.
    - Added the following SG1 issue: 3941.
    - Added the following SG9 issue: 3913.
    - Added the following SG16 issue: 3944.
    - Added the following 30 WP issues: 3833, 3834, 3836, 3839, 3841, 3842, 3843, 3847, 3848, 3849, 3850, 3851, 3853, 3857, 3860, 3862, 3865, 3866, 3867, 3869, 3870, 3871, 3872, 3875, 3876, 3877, 3878, 3879, 3880, 3881.
    - Added the following Resolved issue: 3859.
    - Added the following 2 NAD issues: 3858, 3874.
    - Changed the following issue to Tentatively Ready (from Open): 2994.
    - Changed the following 2 issues to Tentatively NAD (from New): 2457, 3635.
    - Changed the following issue to Tentatively NAD (from LEWG): 3714.
    - Changed the following 2 issues to Open (from Tentatively NAD): 2116, 3357.
    - Changed the following issue to Open (from New): 3507.
    - Changed the following issue to Open (from SG16): 3767.
    - Changed the following 12 issues to WP (from Ready): 2195, 2295, 3032, 3085, 3664, 3720, 3756, 3769, 3807, 3811, 3820, 3825.
    - Changed the following issue to WP (from Tentatively Ready): 3819.
    - Changed the following 10 issues to WP (from New): 3204, 3622, 3631, 3645, 3655, 3723, 3734, 3772, 3790, 3803.
    - Changed the following 5 issues to WP (from Open): 3441, 3733, 3742, 3786, 3821.
    - Changed the following 3 issues to WP (from LEWG): 3810, 3827, 3828.
    - Changed the following 9 issues to Resolved (from New): 3223, 3234, 3412, 3673, 3676, 3700, 3718, 3787, 3791.
    - Changed the following 2 issues to Resolved (from Open): 3442, 3698.
    - Changed the following 4 issues to Resolved (from SG16): 3565, 3576, 3639, 3780.
    - Changed the following 12 issues to NAD (from Tentatively NAD): 2432, 3726, 3727, 3735, 3739, 3740, 3741, 3752, 3768, 3779, 3789, 3800.
    - Changed the following issue to NAD (from New): 3806.
- R123: 2022-11-24 Post-Kona
  - Summary:
    - 405 open issues, up by 50.
    - 2908 closed issues, up by 100.
    - 40 reassigned issues, up by 1.
    - 3353 issues total, up by 151.
  - Details:
    - Added the following 7 Ready issues: 3720, 3756, 3769, 3807, 3811, 3820, 3825.
    - Added the following Tentatively Ready issue: 3819.
    - Added the following 11 Tentatively NAD issues: 3726, 3727, 3735, 3739, 3740, 3741, 3752, 3768, 3779, 3789, 3800.
    - Added the following 51 New issues: 3681, 3682, 3684, 3685, 3686, 3688, 3689, 3690, 3691, 3693, 3694, 3696, 3697, 3699, 3700, 3706, 3716, 3718, 3722, 3723, 3725, 3728, 3729, 3730, 3731, 3734, 3744, 3748, 3749, 3758, 3763, 3772, 3783, 3787, 3790, 3791, 3793, 3794, 3797, 3799, 3802, 3803, 3804, 3805, 3806, 3809, 3812, 3813, 3829, 3830, 3831.
    - Added the following 6 Open issues: 3698, 3733, 3742, 3777, 3786, 3821.
    - Added the following 5 LEWG issues: 3714, 3776, 3810, 3827, 3828.
    - Added the following 2 SG16 issues: 3767, 3780.
    - Added the following 65 WP issues: 3683, 3687, 3692, 3701, 3702, 3703, 3704, 3705, 3707, 3708, 3709, 3710, 3711, 3712, 3713, 3715, 3717, 3719, 3721, 3724, 3732, 3736, 3737, 3738, 3743, 3745, 3746, 3747, 3750, 3751, 3753, 3754, 3755, 3757, 3759, 3760, 3761, 3762, 3764, 3765, 3766, 3770, 3771, 3773, 3774, 3775, 3778, 3781, 3782, 3784, 3785, 3788, 3792, 3795, 3796, 3798, 3801, 3814, 3816, 3817, 3818, 3822, 3823, 3824, 3826.
    - Added the following Resolved issue: 3815.
    - Added the following 2 NAD issues: 3695, 3808.
    - Changed the following 3 issues to Ready (from New): 2295, 3032, 3664.
    - Changed the following 2 issues to Ready (from Open): 2195, 3085.
    - Changed the following 2 issues to Tentatively NAD (from Open): 2116, 3357.
    - Changed the following issue to Tentatively NAD (from EWG): 2432.
    - Changed the following issue to Open (from New): 3167.
    - Changed the following issue to Open (from NAD): 617.
    - Changed the following issue to LEWG (from New): 3679.
    - Changed the following 3 issues to WP (from Tentatively Ready): 3670, 3671, 3672.
    - Changed the following 13 issues to WP (from New): 3028, 3118, 3136, 3177, 3411, 3569, 3594, 3597, 3600, 3617, 3629, 3636, 3677.
    - Changed the following 2 issues to WP (from Open): 3545, 3656.
    - Changed the following 2 issues to WP (from LEWG): 3515, 3646.
    - Changed the following issue to WP (from SG1): 3659.
    - Changed the following issue to WP (from SG9): 3564.
    - Changed the following 2 issues to Resolved (from New): 2561, 2564.
    - Changed the following 2 issues to Resolved (from Open): 2114, 2349.
    - Changed the following issue to Resolved (from EWG): 2813.
    - Changed the following issue to TS (from Tentatively Ready): 3649.
    - Changed the following 2 issues to NAD (from Tentatively NAD): 3579, 3667.
    - Changed the following issue to NAD (from New): 2352.
    - Changed the following issue to NAD (from Open): 3068.
    - Changed the following issue to NAD (from SG1): 3611.
- R122: 2022-03-04 March 2022
  - Summary:
    - 355 open issues, up by 41.
    - 2808 closed issues, up by 222.
    - 39 reassigned issues, up by 4.
    - 3202 issues total, up by 267.
  - Details:
    - Added the following 4 Tentatively Ready issues: 3649, 3670, 3671, 3672.
    - Added the following 2 Tentatively NAD issues: 3579, 3667.
    - Added the following 103 New issues: 3416, 3418, 3423, 3424, 3429, 3431, 3436, 3438, 3439, 3444, 3451, 3456, 3457, 3459, 3463, 3475, 3484, 3487, 3489, 3491, 3493, 3496, 3497, 3499, 3501, 3503, 3504, 3507, 3508, 3511, 3512, 3513, 3516, 3531, 3537, 3538, 3550, 3556, 3569, 3577, 3578, 3582, 3583, 3584, 3586, 3587, 3594, 3597, 3599, 3600, 3602, 3603, 3604, 3605, 3606, 3608, 3609, 3613, 3614, 3615, 3617, 3620, 3622, 3623, 3624, 3625, 3626, 3627, 3628, 3629, 3630, 3631, 3633, 3634, 3635, 3636, 3637, 3638, 3640, 3641, 3642, 3644, 3645, 3647, 3651, 3653, 3655, 3658, 3662, 3663, 3664, 3665, 3666, 3668, 3669, 3673, 3674, 3675, 3676, 3677, 3678, 3679, 3680.
    - Added the following 5 Open issues: 3441, 3442, 3488, 3545, 3656.
    - Added the following 6 LEWG issues: 3445, 3454, 3486, 3515, 3534, 3646.
    - Added the following 4 SG1 issues: 3417, 3485, 3611, 3659.
    - Added the following SG9 issue: 3564.
    - Added the following 3 SG16 issues: 3565, 3576, 3639.
    - Added the following 120 WP issues: 3414, 3419, 3420, 3421, 3422, 3425, 3426, 3427, 3428, 3430, 3432, 3433, 3434, 3435, 3437, 3443, 3446, 3447, 3448, 3449, 3450, 3453, 3455, 3460, 3461, 3462, 3464, 3465, 3466, 3467, 3470, 3471, 3472, 3473, 3474, 3476, 3477, 3480, 3481, 3482, 3483, 3490, 3492, 3494, 3495, 3498, 3500, 3502, 3505, 3506, 3517, 3518, 3519, 3520, 3521, 3522, 3523, 3525, 3526, 3527, 3528, 3529, 3530, 3532, 3533, 3535, 3536, 3539, 3540, 3541, 3542, 3543, 3544, 3546, 3548, 3549, 3551, 3552, 3553, 3554, 3555, 3557, 3559, 3560, 3561, 3563, 3566, 3567, 3568, 3570, 3571, 3572, 3573, 3574, 3580, 3581, 3585, 3589, 3590, 3591, 3592, 3593, 3595, 3598, 3601, 3607, 3610, 3612, 3616, 3618, 3619, 3621, 3632, 3643, 3648, 3650, 3654, 3657, 3660, 3661.
    - Added the following 11 Resolved issues: 3452, 3458, 3469, 3478, 3479, 3509, 3510, 3514, 3524, 3547, 3575.
    - Added the following NAD Editorial issue: 3558.
    - Added the following 7 NAD issues: 3415, 3440, 3468, 3562, 3588, 3596, 3652.
    - Changed the following 2 issues to Open (from LEWG): 3068, 3357.
    - Changed the following issue to Open (from NAD): 1102.
    - Changed the following issue to NAD Future (from LEWG): 1025.
    - Changed the following 4 issues to WP (from Ready): 3117, 3211, 3236, 3249.
    - Changed the following issue to WP (from Tentatively Ready): 2820.
    - Changed the following 22 issues to WP (from New): 2191, 2743, 2774, 2997, 3088, 3123, 3143, 3146, 3152, 3195, 3265, 3293, 3306, 3361, 3391, 3403, 3404, 3405, 3406, 3407, 3410, 3413.
    - Changed the following 10 issues to WP (from Open): 2381, 2731, 2762, 2818, 2839, 3036, 3120, 3121, 3170, 3171.
    - Changed the following issue to WP (from LEWG): 3392.
    - Changed the following 2 issues to C++20 (from New): 3290, 3386.
    - Changed the following 280 issues to C++20 (from WP): 1203, 2139, 2164, 2183, 2184, 2243, 2412, 2444, 2593, 2597, 2682, 2783, 2816, 2843, 2849, 2851, 2859, 2870, 2899, 2932, 2935, 2936, 2937, 2940, 2941, 2942, 2943, 2944, 2945, 2946, 2948, 2950, 2952, 2953, 2954, 2958, 2961, 2964, 2965, 2966, 2969, 2970, 2972, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2988, 2989, 2993, 2995, 2996, 2998, 3000, 3001, 3004, 3005, 3007, 3008, 3009, 3012, 3013, 3014, 3015, 3017, 3018, 3024, 3025, 3026, 3030, 3031, 3034, 3035, 3037, 3038, 3039, 3040, 3041, 3042, 3043, 3045, 3048, 3050, 3051, 3054, 3055, 3058, 3062, 3065, 3067, 3070, 3074, 3075, 3076, 3077, 3079, 3080, 3083, 3087, 3094, 3096, 3100, 3101, 3102, 3103, 3104, 3112, 3116, 3119, 3122, 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3137, 3140, 3141, 3144, 3145, 3147, 3148, 3149, 3150, 3153, 3154, 3158, 3160, 3169, 3173, 3175, 3179, 3180, 3182, 3183, 3184, 3185, 3186, 3187, 3190, 3191, 3194, 3196, 3198, 3199, 3200, 3201, 3202, 3206, 3208, 3209, 3218, 3221, 3222, 3224, 3225, 3226, 3230, 3231, 3232, 3233, 3235, 3237, 3238, 3241, 3242, 3243, 3244, 3245, 3246, 3247, 3248, 3250, 3251, 3252, 3253, 3254, 3255, 3256, 3257, 3259, 3260, 3262, 3264, 3266, 3269, 3270, 3272, 3273, 3274, 3276, 3277, 3280, 3281, 3282, 3284, 3285, 3286, 3291, 3292, 3294, 3296, 3299, 3300, 3301, 3302, 3303, 3304, 3307, 3310, 3313, 3314, 3315, 3316, 3317, 3318, 3319, 3320, 3321, 3323, 3324, 3325, 3326, 3327, 3328, 3329, 3330, 3331, 3332, 3334, 3335, 3338, 3340, 3346, 3347, 3348, 3349, 3350, 3351, 3352, 3354, 3355, 3356, 3358, 3359, 3360, 3362, 3363, 3364, 3367, 3369, 3371, 3372, 3373, 3374, 3375, 3377, 3379, 3380, 3381, 3382, 3383, 3384, 3385, 3387, 3388, 3389, 3390, 3393, 3395, 3396, 3397, 3398.
    - Changed the following issue to Resolved (from Tentatively Ready): 3368.
    - Changed the following 16 issues to Resolved (from New): 2154, 2375, 3006, 3110, 3135, 3156, 3212, 3213, 3258, 3278, 3283, 3289, 3345, 3366, 3376, 3408.
    - Changed the following 4 issues to Resolved (from Open): 2363, 2741, 3155, 3168.
    - Changed the following 5 issues to Resolved (from LEWG): 2690, 2814, 2825, 3052, 3228.
    - Changed the following 10 issues to Resolved (from Tentatively Resolved): 2155, 2292, 2498, 2808, 2832, 2999, 3125, 3151, 3178, 3322.
    - Changed the following 5 issues to NAD (from Tentatively NAD): 2335, 3207, 3365, 3394, 3399.
    - Changed the following issue to NAD (from Open): 1396.
    - Changed the following issue to NAD (from LEWG): 3165.
- R121: 2020-03-02 post-Prague mailing
  - Summary:
    - 314 open issues, down by 66.
    - 2586 closed issues, up by 114.
    - 35 reassigned issues, up by 0.
    - 2935 issues total, up by 48.
  - Details:
    - Added the following Tentatively Ready issue: 3368.
    - Added the following 2 Tentatively NAD issues: 3394, 3399.
    - Added the following 20 New issues: 3366, 3370, 3376, 3378, 3386, 3391, 3400, 3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413.
    - Added the following LEWG issue: 3392.
    - Added the following 24 WP issues: 3367, 3369, 3371, 3372, 3373, 3374, 3375, 3377, 3379, 3380, 3381, 3382, 3383, 3384, 3385, 3387, 3388, 3389, 3390, 3393, 3395, 3396, 3397, 3398.
    - Changed the following 4 issues to Ready (from New): 3117, 3211, 3236, 3249.
    - Changed the following issue to Tentatively Ready (from Open): 2820.
    - Changed the following 2 issues to Tentatively NAD (from New): 2335, 3365.
    - Changed the following 2 issues to Open (from New): 2844, 3305.
    - Changed the following 2 issues to Tentatively Resolved (from New): 2498, 3151.
    - Changed the following issue to Tentatively Resolved (from Open): 3125.
    - Changed the following 25 issues to WP (from Ready): 3264, 3280, 3281, 3292, 3302, 3303, 3304, 3307, 3310, 3313, 3315, 3316, 3317, 3318, 3319, 3320, 3321, 3323, 3324, 3325, 3326, 3327, 3329, 3330, 3331.
    - Changed the following 19 issues to WP (from Tentatively Ready): 3194, 3233, 3254, 3284, 3285, 3286, 3291, 3294, 3296, 3299, 3300, 3332, 3338, 3346, 3349, 3350, 3351, 3356, 3360.
    - Changed the following 36 issues to WP (from New): 3018, 3141, 3150, 3175, 3200, 3201, 3226, 3237, 3238, 3242, 3243, 3247, 3248, 3250, 3251, 3252, 3255, 3260, 3262, 3269, 3270, 3301, 3314, 3328, 3334, 3335, 3340, 3347, 3348, 3352, 3355, 3358, 3359, 3362, 3363, 3364.
    - Changed the following 4 issues to WP (from Open): 1203, 2859, 3050, 3282.
    - Changed the following issue to WP (from LEWG): 3354.
    - Changed the following issue to Resolved (from New): 3336.
    - Changed the following 3 issues to NAD (from New): 3271, 3298, 3333.
    - Changed the following issue to NAD (from Open): 2860.
- R120: 2020-01-13 pre-Prague mailing
  - Summary:
    - 380 open issues, up by 4.
    - 2472 closed issues, up by 7.
    - 35 reassigned issues, up by 0.
    - 2887 issues total, up by 11.
  - Details:
    - Added the following 2 Tentatively Ready issues: 3356, 3360.
    - Added the following 8 New issues: 3355, 3358, 3359, 3361, 3362, 3363, 3364, 3365.
    - Added the following LEWG issue: 3357.
    - Changed the following 2 issues to Ready (from New): 3280, 3292.
    - Changed the following issue to Ready (from LEWG): 3304.
    - Changed the following 5 issues to Tentatively Ready (from New): 3194, 3254, 3349, 3350, 3351.
    - Changed the following issue to LEWG (from New): 3354.
    - Changed the following issue to Tentatively Resolved (from Ready): 3322.
    - Changed the following 3 issues to Resolved (from New): 2957, 3023, 3176.
    - Changed the following 3 issues to Resolved (from Open): 2894, 3061, 3295.
    - Changed the following issue to Resolved (from EWG): 2089.
- R119: 2019-12-09 post-Belfast mailing
  - Summary:
    - 376 open issues, up by 25.
    - 2465 closed issues, up by 32.
    - 35 reassigned issues, down by 1.
    - 2876 issues total, up by 56.
  - Details:
    - Added the following 21 Ready issues: 3302, 3303, 3307, 3310, 3313, 3315, 3316, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327, 3329, 3330, 3331.
    - Added the following 5 Tentatively Ready issues: 3299, 3300, 3332, 3338, 3346.
    - Added the following 27 New issues: 3301, 3305, 3306, 3308, 3309, 3314, 3328, 3333, 3334, 3335, 3336, 3337, 3339, 3340, 3341, 3342, 3343, 3344, 3345, 3347, 3348, 3349, 3350, 3351, 3352, 3353, 3354.
    - Added the following LEWG issue: 3304.
    - Added the following 2 Dup issues: 3311, 3312.
    - Changed the following issue to Ready (from New): 3264.
    - Changed the following issue to Ready (from LEWG): 3281.
    - Changed the following 7 issues to Tentatively Ready (from New): 3233, 3284, 3285, 3286, 3291, 3294, 3296.
    - Changed the following issue to Open (from New): 3295.
    - Changed the following issue to Open (from LEWG): 3282.
    - Changed the following issue to LEWG (from New): 3228.
    - Changed the following 4 issues to WP (from Ready): 3070, 3103, 3149, 3190.
    - Changed the following 23 issues to WP (from Tentatively Ready): 3218, 3221, 3222, 3224, 3225, 3230, 3231, 3232, 3235, 3241, 3244, 3245, 3246, 3253, 3256, 3257, 3259, 3266, 3272, 3273, 3274, 3276, 3277.
    - Changed the following 2 issues to Resolved (from New): 3239, 3279.
    - Changed the following issue to Resolved (from SG1): 2334.
- R118: 2019-10-06 pre-Belfast mailing
  - Summary:
    - 351 open issues, up by 48.
    - 2433 closed issues, up by 0.
    - 36 reassigned issues, up by 2.
    - 2820 issues total, up by 50.
  - Details:
    - Added the following 10 Tentatively Ready issues: 3253, 3256, 3257, 3259, 3266, 3272, 3273, 3274, 3276, 3277.
    - Added the following 38 New issues: 3249, 3250, 3251, 3252, 3254, 3255, 3258, 3260, 3261, 3262, 3263, 3264, 3265, 3267, 3268, 3269, 3270, 3271, 3275, 3278, 3279, 3280, 3283, 3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296, 3297, 3298.
    - Added the following 2 LEWG issues: 3281, 3282.
    - Changed the following 5 issues to Tentatively Ready (from New): 3235, 3241, 3244, 3245, 3246.
- R117: 2019-08-05 post-Cologne mailing
  - Summary:
    - 303 open issues, up by 8.
    - 2433 closed issues, up by 20.
    - 34 reassigned issues, down by 1.
    - 2770 issues total, up by 27.
  - Details:
    - Added the following 6 Tentatively Ready issues: 3222, 3224, 3225, 3230, 3231, 3232.
    - Added the following 21 New issues: 3223, 3226, 3227, 3228, 3229, 3233, 3234, 3235, 3236, 3237, 3238, 3239, 3240, 3241, 3242, 3243, 3244, 3245, 3246, 3247, 3248.
    - Changed the following 2 issues to Ready (from New): 3070, 3190.
    - Changed the following 2 issues to Ready (from Open): 3103, 3149.
    - Changed the following 2 issues to Tentatively Ready (from New): 3218, 3221.
    - Changed the following 2 issues to Open (from New): 2818, 3170.
    - Changed the following 6 issues to WP (from Ready): 2899, 3055, 3158, 3169, 3186, 3187.
    - Changed the following 11 issues to WP (from Tentatively Ready): 3183, 3184, 3185, 3191, 3196, 3198, 3199, 3202, 3206, 3208, 3209.
    - Changed the following issue to Resolved (from New): 3091.
    - Changed the following issue to NAD (from New): 3139.
    - Changed the following issue to NAD (from LEWG): 3138.
- R116: 2019-06-17 pre-Cologne mailing
  - Summary:
    - 295 open issues, up by 25.
    - 2413 closed issues, up by 0.
    - 35 reassigned issues, up by 0.
    - 2743 issues total, up by 25.
  - Details:
    - Added the following 6 Tentatively Ready issues: 3198, 3199, 3202, 3206, 3208, 3209.
    - Added the following Tentatively NAD issue: 3207.
    - Added the following 18 New issues: 3197, 3200, 3201, 3203, 3204, 3205, 3210, 3211, 3212, 3213, 3214, 3215, 3216, 3217, 3218, 3219, 3220, 3221.
    - Changed the following 2 issues to Tentatively Ready (from New): 3191, 3196.
- R115: 2019-04-02 post-Kona mailing
  - Summary:
    - 270 open issues, down by 19.
    - 2413 closed issues, up by 31.
    - 35 reassigned issues, up by 0.
    - 2718 issues total, up by 12.
  - Details:
    - Added the following 2 Ready issues: 3186, 3187.
    - Added the following Tentatively Ready issue: 3185.
    - Added the following 9 New issues: 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3195, 3196.
    - Changed the following 3 issues to Ready (from New): 3055, 3158, 3169.
    - Changed the following issue to Ready (from Open): 2899.
    - Changed the following 2 issues to Tentatively Ready (from New): 3183, 3184.
    - Changed the following 4 issues to Open (from New): 3036, 3161, 3168, 3171.
    - Changed the following 5 issues to Tentatively Resolved (from New): 2292, 2808, 2832, 2999, 3178.
    - Changed the following issue to Tentatively Resolved (from Open): 2155.
    - Changed the following 2 issues to WP (from Ready): 3012, 3119.
    - Changed the following 11 issues to WP (from Tentatively Ready): 3040, 3077, 3087, 3101, 3112, 3133, 3144, 3173, 3179, 3180, 3182.
    - Changed the following 2 issues to Resolved (from New): 2951, 3113.
    - Changed the following 16 issues to NAD (from Tentatively NAD): 708, 935, 1121, 1154, 1188, 1217, 1235, 1282, 1289, 1317, 1406, 1499, 2226, 2417, 2600, 3106.
- R114: 2019-01-21 pre-Kona mailing
  - Summary:
    - 289 open issues, up by 14.
    - 2382 closed issues, up by 1.
    - 35 reassigned issues, up by 0.
    - 2706 issues total, up by 15.
  - Details:
    - Added the following 4 Tentatively Ready issues: 3173, 3179, 3180, 3182.
    - Added the following 10 New issues: 3170, 3171, 3172, 3174, 3175, 3176, 3177, 3178, 3183, 3184.
    - Added the following NAD issue: 3181.
    - Changed the following 4 issues to Tentatively Ready (from New): 3077, 3112, 3133, 3144.
- R113: 2018-11-26 post-San Diego mailing
  - Summary:
    - 275 open issues, down by 45.
    - 2381 closed issues, up by 52.
    - 35 reassigned issues, up by 0.
    - 2691 issues total, up by 7.
  - Details:
    - Added the following 4 New issues: 3166, 3167, 3168, 3169.
    - Added the following LEWG issue: 3165.
    - Added the following 2 NAD issues: 3163, 3164.
    - Changed the following issue to Ready (from New): 3119.
    - Changed the following issue to Ready (from Open): 3012.
    - Changed the following issue to Tentatively Ready (from New): 3087.
    - Changed the following issue to Tentatively Ready (from Open): 3040.
    - Changed the following issue to Tentatively Ready (from LEWG): 3101.
    - Changed the following 7 issues to Open (from New): 2962, 2994, 3057, 3061, 3103, 3121, 3149.
    - Changed the following 2 issues to Open (from Core): 2859, 2860.
    - Changed the following issue to LEWG (from New): 3138.
    - Changed the following issue to LEWG (from Open): 2307.
    - Changed the following 10 issues to WP (from Ready): 2183, 2184, 2412, 2682, 2697, 2936, 2995, 2996, 3054, 3116.
    - Changed the following 24 issues to WP (from Tentatively Ready): 2943, 2960, 3008, 3025, 3031, 3037, 3038, 3065, 3096, 3122, 3127, 3128, 3129, 3130, 3131, 3132, 3137, 3140, 3145, 3147, 3148, 3153, 3154, 3160.
    - Changed the following issue to Resolved (from Tentatively Ready): 3134.
    - Changed the following 6 issues to Resolved (from New): 2318, 2836, 2841, 2929, 3022, 3115.
    - Changed the following 6 issues to Resolved (from Open): 1052, 2151, 2499, 2780, 2797, 3111.
    - Changed the following 2 issues to NAD (from New): 2563, 3016.
    - Changed the following issue to NAD (from Open): 1242.
- R112: 2018-10-08 pre-San Diego mailing
  - Summary:
    - 320 open issues, up by 36.
    - 2329 closed issues, up by 3.
    - 35 reassigned issues, up by 0.
    - 2684 issues total, up by 39.
  - Details:
    - Added the following 15 Tentatively Ready issues: 3127, 3128, 3129, 3130, 3131, 3132, 3134, 3137, 3140, 3145, 3147, 3148, 3153, 3154, 3160.
    - Added the following 22 New issues: 3124, 3126, 3133, 3135, 3136, 3138, 3139, 3141, 3142, 3143, 3144, 3146, 3149, 3150, 3151, 3152, 3156, 3157, 3158, 3159, 3161, 3162.
    - Added the following 2 Open issues: 3125, 3155.
    - Changed the following 6 issues to Tentatively Ready (from New): 2960, 3025, 3031, 3037, 3065, 3096.
    - Changed the following 3 issues to Tentatively Ready (from Open): 2943, 3008, 3038.
    - Changed the following issue to Tentatively NAD (from New): 3106.
    - Changed the following 5 issues to Open (from New): 2220, 3081, 3085, 3092, 3120.
    - Changed the following issue to EWG (from Open): 2813.
    - Changed the following issue to Resolved (from Tentatively NAD): 1150.
    - Changed the following issue to Resolved (from Open): 2840.
    - Changed the following issue to Resolved (from LEWG): 1201.
- R111: 2018-06-25 post-Rapperswil mailing
  - Summary:
    - 284 open issues, up by 15.
    - 2326 closed issues, up by 25.
    - 35 reassigned issues, down by 26.
    - 2645 issues total, up by 14.
  - Details:
    - Added the following Ready issue: 3116.
    - Added the following Tentatively Ready issue: 3122.
    - Added the following 10 New issues: 3110, 3112, 3113, 3115, 3117, 3118, 3119, 3120, 3121, 3123.
    - Added the following Open issue: 3111.
    - Added the following LEWG issue: 3114.
    - Changed the following 4 issues to Ready (from Review): 2412, 2682, 2697, 2936.
    - Changed the following 4 issues to Ready (from New): 2183, 2184, 2995, 3054.
    - Changed the following issue to Ready (from LEWG): 2996.
    - Changed the following 16 issues to Tentatively NAD (from LEWG): 708, 935, 1121, 1150, 1154, 1188, 1217, 1235, 1282, 1289, 1317, 1406, 1499, 2226, 2417, 2600.
    - Changed the following issue to Open (from Review): 2708.
    - Changed the following 7 issues to Open (from New): 2173, 2286, 2829, 3008, 3038, 3050, 3082.
    - Changed the following 11 issues to Open (from LEWG): 423, 523, 1052, 1203, 1238, 1242, 1396, 1521, 2471, 2762, 2780.
    - Changed the following 3 issues to LEWG (from New): 3052, 3068, 3101.
    - Changed the following 3 issues to WP (from Ready): 2139, 3058, 3076.
    - Changed the following 12 issues to WP (from Tentatively Ready): 2970, 3062, 3067, 3071, 3074, 3079, 3080, 3083, 3094, 3100, 3102, 3104.
    - Changed the following 4 issues to Resolved (from New): 2511, 2693, 2776, 2938.
    - Changed the following 2 issues to Resolved (from Open): 2800, 2897.
    - Changed the following 2 issues to Resolved (from LEWG): 2040, 2419.
    - Changed the following 2 issues to NAD (from New): 2121, 2892.
- R110: 2018-05-07 pre-Rapperswil mailing
  - Summary:
    - 269 open issues, up by 17.
    - 2301 closed issues, up by 0.
    - 61 reassigned issues, up by 0.
    - 2631 issues total, up by 17.
  - Details:
    - Added the following 4 Tentatively Ready issues: 3094, 3100, 3102, 3104.
    - Added the following 13 New issues: 3093, 3095, 3096, 3097, 3098, 3099, 3101, 3103, 3105, 3106, 3107, 3108, 3109.
    - No issues changed.
- R109: 2018-04-02 post-Jacksonville mailing
  - Summary:
    - 252 open issues, down by 5.
    - 2301 closed issues, up by 36.
    - 61 reassigned issues, down by 1.
    - 2614 issues total, up by 30.
  - Details:
    - Added the following Ready issue: 3076.
    - Added the following 6 Tentatively Ready issues: 3067, 3071, 3074, 3079, 3080, 3083.
    - Added the following 22 New issues: 3063, 3064, 3065, 3066, 3068, 3069, 3070, 3072, 3073, 3077, 3078, 3081, 3082, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3091, 3092.
    - Added the following WP issue: 3075.
    - Changed the following issue to Ready (from New): 3058.
    - Changed the following issue to Ready (from Open): 2139.
    - Changed the following 2 issues to Tentatively Ready (from New): 2970, 3062.
    - Changed the following 2 issues to Open (from New): 2823, 3049.
    - Changed the following issue to SG1 (from New): 2506.
    - Changed the following 5 issues to WP (from Ready): 2843, 2969, 2975, 3014, 3030.
    - Changed the following 27 issues to WP (from Tentatively Ready): 2164, 2243, 2816, 2849, 2851, 2989, 3000, 3002, 3004, 3005, 3007, 3009, 3010, 3013, 3015, 3017, 3020, 3026, 3034, 3035, 3039, 3041, 3042, 3043, 3045, 3048, 3051.
    - Changed the following issue to WP (from New): 2946.
    - Changed the following 2 issues to Resolved (from LEWG): 2831, 2968.
- R108: 2018-02-12 pre-Jacksonville mailing
  - Summary:
    - 257 open issues, up by 16.
    - 2265 closed issues, up by 7.
    - 62 reassigned issues, up by 0.
    - 2584 issues total, up by 23.
  - Details:
    - Added the following 6 Tentatively Ready issues: 3041, 3042, 3043, 3045, 3048, 3051.
    - Added the following 16 New issues: 3044, 3046, 3047, 3049, 3050, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062.
    - Added the following Open issue: 3040.
    - Changed the following 6 issues to Tentatively Ready (from New): 2243, 2816, 2849, 2851, 2989, 3015.
    - Changed the following issue to Tentatively Ready (from Open): 2164.
    - Changed the following 3 issues to Review (from Open): 2682, 2708, 2936.
    - Changed the following 4 issues to Open (from New): 2839, 2840, 2990, 3003.
    - Changed the following 2 issues to C++17 (from Open): 2569, 2587.
    - Changed the following 3 issues to Resolved (from New): 2734, 2821, 2856.
    - Changed the following issue to Resolved (from Open): 2055.
    - Changed the following issue to NAD (from New): 2772.
- R107: 2017-11-28 2017 post-Albuquerque mailing
  - Summary:
    - 241 open issues, down by 16.
    - 2258 closed issues, up by 29.
    - 62 reassigned issues, up by 0.
    - 2561 issues total, up by 13.
  - Details:
    - Added the following Ready issue: 3030.
    - Added the following 3 Tentatively Ready issues: 3034, 3035, 3039.
    - Added the following 7 New issues: 3027, 3028, 3031, 3032, 3036, 3037, 3038.
    - Added the following Open issue: 3029.
    - Added the following NAD Editorial issue: 3033.
    - Changed the following 4 issues to Ready (from New): 2843, 2969, 2975, 3014.
    - Changed the following 11 issues to Tentatively Ready (from New): 3000, 3002, 3004, 3005, 3007, 3009, 3010, 3013, 3017, 3020, 3026.
    - Changed the following 6 issues to Open (from New): 2800, 2833, 2897, 2943, 3011, 3012.
    - Changed the following 23 issues to WP (from Ready): 2779, 2870, 2935, 2941, 2944, 2945, 2948, 2950, 2952, 2953, 2964, 2965, 2972, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2988, 2993, 2998.
    - Changed the following 2 issues to WP (from Tentatively Ready): 3001, 3024.
    - Changed the following issue to WP (from New): 2958.
    - Changed the following 2 issues to Resolved (from New): 2541, 2799.
- R106: 2017-10-16 2017 pre-Albuquerque mailing
  - Summary:
    - 257 open issues, up by 22.
    - 2229 closed issues, up by 0.
    - 62 reassigned issues, up by 0.
    - 2548 issues total, up by 22.
  - Details:
    - Added the following Tentatively Ready issue: 3024.
    - Added the following 21 New issues: 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3025, 3026.
    - No issues changed.
- R105: 2017-07-30 2017 post-Toronto mailing
  - Summary:
    - 235 open issues, down by 22.
    - 2229 closed issues, up by 36.
    - 62 reassigned issues, up by 8.
    - 2526 issues total, up by 22.
  - Details:
    - Added the following 3 Ready issues: 2988, 2993, 2998.
    - Added the following Tentatively Ready issue: 3001.
    - Added the following 14 New issues: 2983, 2984, 2986, 2987, 2989, 2990, 2994, 2995, 2997, 2999, 3000, 3002, 3003, 3004.
    - Added the following 3 LEWG issues: 2985, 2991, 2996.
    - Added the following NAD issue: 2992.
    - Changed the following 19 issues to Ready (from New): 2870, 2935, 2941, 2944, 2945, 2948, 2950, 2952, 2953, 2964, 2965, 2972, 2976, 2977, 2978, 2979, 2980, 2981, 2982.
    - Changed the following issue to Ready (from LEWG): 2779.
    - Changed the following 6 issues to Open (from New): 2731, 2741, 2813, 2820, 2899, 2936.
    - Changed the following 6 issues to LEWG (from New): 2883, 2884, 2885, 2922, 2968, 2973.
    - Changed the following issue to WP (from Ready): 2932.
    - Changed the following 11 issues to WP (from Tentatively Ready): 2444, 2593, 2597, 2783, 2937, 2940, 2942, 2954, 2961, 2966, 2974.
    - Changed the following 2 issues to C++17 (from New): 2901, 2956.
    - Changed the following 279 issues to C++17 (from WP): 1169, 2016, 2059, 2062, 2063, 2072, 2076, 2101, 2106, 2111, 2119, 2127, 2129, 2133, 2156, 2160, 2166, 2168, 2170, 2181, 2192, 2212, 2217, 2218, 2219, 2221, 2223, 2224, 2230, 2233, 2234, 2239, 2244, 2250, 2259, 2260, 2261, 2266, 2273, 2276, 2296, 2309, 2310, 2312, 2325, 2328, 2336, 2340, 2353, 2354, 2361, 2364, 2365, 2367, 2369, 2376, 2377, 2378, 2380, 2384, 2385, 2387, 2393, 2394, 2396, 2399, 2400, 2401, 2403, 2404, 2406, 2407, 2408, 2411, 2415, 2420, 2422, 2425, 2426, 2427, 2428, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2447, 2448, 2450, 2454, 2455, 2458, 2459, 2460, 2462, 2464, 2466, 2467, 2468, 2469, 2470, 2473, 2475, 2476, 2477, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2492, 2495, 2503, 2510, 2514, 2519, 2520, 2523, 2531, 2534, 2536, 2537, 2540, 2542, 2544, 2545, 2549, 2550, 2556, 2557, 2559, 2560, 2562, 2565, 2566, 2567, 2571, 2572, 2576, 2577, 2578, 2579, 2581, 2582, 2583, 2584, 2585, 2586, 2589, 2590, 2591, 2596, 2598, 2664, 2667, 2669, 2670, 2671, 2672, 2673, 2674, 2676, 2678, 2679, 2680, 2681, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2694, 2696, 2698, 2699, 2704, 2706, 2707, 2709, 2710, 2711, 2712, 2716, 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2732, 2735, 2736, 2738, 2739, 2740, 2742, 2744, 2747, 2748, 2749, 2752, 2755, 2756, 2758, 2759, 2760, 2765, 2767, 2768, 2769, 2770, 2771, 2773, 2777, 2778, 2781, 2782, 2784, 2785, 2786, 2787, 2788, 2789, 2790, 2793, 2794, 2795, 2796, 2801, 2802, 2804, 2806, 2807, 2812, 2824, 2826, 2834, 2835, 2837, 2838, 2842, 2850, 2853, 2855, 2857, 2861, 2866, 2868, 2872, 2873, 2874, 2875, 2876, 2878, 2890, 2900, 2903, 2904, 2905, 2908, 2911, 2921, 2934.
    - Changed the following 2 issues to Resolved (from New): 2880, 2955.
    - Changed the following 3 issues to Resolved (from Open): 2070, 2368, 2665.
    - Changed the following issue to Resolved (from Pending Resolved): 2798.
    - Changed the following 4 issues to NAD (from Tentatively NAD): 760, 2337, 2692, 2717.
    - Changed the following 11 issues to NAD (from New): 2852, 2871, 2886, 2891, 2893, 2902, 2907, 2909, 2916, 2967, 2971.
    - Changed the following issue to Dup (from New): 2910.
    - Changed the following 77 issues to TS (from WP): 2371, 2374, 2389, 2390, 2395, 2409, 2410, 2418, 2451, 2463, 2494, 2500, 2509, 2515, 2516, 2517, 2518, 2521, 2522, 2525, 2526, 2527, 2539, 2551, 2555, 2558, 2568, 2570, 2573, 2574, 2575, 2588, 2601, 2602, 2603, 2605, 2606, 2607, 2608, 2609, 2614, 2615, 2616, 2618, 2619, 2621, 2622, 2624, 2625, 2627, 2629, 2632, 2633, 2634, 2635, 2636, 2637, 2640, 2641, 2644, 2645, 2647, 2648, 2649, 2650, 2652, 2653, 2655, 2656, 2657, 2658, 2660, 2662, 2733, 2745, 2750, 2792.
- R104: 2017-06-18 2017 pre-Toronto mailing
  - Summary:
    - 257 open issues, up by 38.
    - 2193 closed issues, up by 17.
    - 54 reassigned issues, down by 19.
    - 2504 issues total, up by 36.
  - Details:
    - Added the following 4 Tentatively Ready issues: 2954, 2961, 2966, 2974.
    - Added the following 32 New issues: 2947, 2948, 2949, 2950, 2951, 2952, 2953, 2955, 2956, 2957, 2958, 2959, 2960, 2962, 2963, 2964, 2965, 2967, 2968, 2969, 2970, 2971, 2972, 2973, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982.
    - Changed the following 2 issues to Tentatively Ready (from New): 2940, 2942.
    - Changed the following issue to Tentatively Ready (from LEWG): 2593.
    - Changed the following issue to Open (from New): 2797.
    - Changed the following 3 issues to Open (from LEWG): 484, 1422, 2055.
    - Changed the following issue to Pending Resolved (from Open): 2798.
    - Changed the following issue to Resolved (from LEWG): 2232.
    - Changed the following issue to NAD (from New): 2898.
    - Changed the following 14 issues to NAD (from LEWG): 255, 851, 877, 933, 1031, 1053, 1112, 1120, 1184, 1320, 2242, 2430, 2446, 2854.
- R103: 2017-03-20 2017 post-Kona mailing
  - Summary:
    - 219 open issues, down by 86.
    - 2176 closed issues, up by 104.
    - 73 reassigned issues, up by 0.
    - 2468 issues total, up by 18.
  - Details:
    - Added the following Ready issue: 2932.
    - Added the following Tentatively Ready issue: 2937.
    - Added the following 13 New issues: 2929, 2933, 2935, 2936, 2938, 2939, 2940, 2941, 2942, 2943, 2944, 2945, 2946.
    - Added the following Open issue: 2931.
    - Added the following WP issue: 2934.
    - Added the following NAD issue: 2930.
    - Changed the following issue to Tentatively Ready (from New): 2783.
    - Changed the following 2 issues to Tentatively Ready (from Open): 2444, 2597.
    - Changed the following issue to Review (from SG1): 2412.
    - Changed the following issue to Open (from New): 2894.
    - Changed the following 2 issues to Core (from New): 2859, 2860.
    - Changed the following 6 issues to WP (from Ready): 2781, 2782, 2784, 2785, 2786, 2787.
    - Changed the following 18 issues to WP (from Tentatively Ready): 2260, 2768, 2769, 2789, 2794, 2795, 2804, 2812, 2824, 2826, 2834, 2835, 2837, 2838, 2842, 2850, 2853, 2855.
    - Changed the following 3 issues to WP (from Review): 2676, 2790, 2796.
    - Changed the following 22 issues to WP (from New): 2788, 2801, 2802, 2806, 2807, 2861, 2866, 2868, 2872, 2873, 2874, 2875, 2876, 2878, 2890, 2900, 2903, 2904, 2905, 2908, 2911, 2921.
    - Changed the following issue to WP (from LEWG): 2857.
    - Changed the following issue to Resolved (from Review): 2245.
    - Changed the following 27 issues to Resolved (from New): 2715, 2803, 2862, 2863, 2864, 2867, 2869, 2877, 2879, 2882, 2887, 2888, 2889, 2895, 2912, 2913, 2914, 2915, 2917, 2918, 2919, 2920, 2924, 2925, 2926, 2927, 2928.
    - Changed the following 19 issues to Resolved (from Tentatively Resolved): 839, 1041, 2179, 2208, 2241, 2294, 2343, 2370, 2391, 2424, 2443, 2501, 2502, 2505, 2529, 2548, 2663, 2677, 2757.
    - Changed the following 3 issues to NAD (from New): 2256, 2449, 2865.
    - Changed the following 2 issues to Dup (from New): 2764, 2896.
- R102: 2017-02-06 2017 pre-Kona mailing
  - Summary:
    - 305 open issues, up by 89.
    - 2072 closed issues, up by 9.
    - 73 reassigned issues, up by 5.
    - 2450 issues total, up by 103.
  - Details:
    - Added the following 9 Tentatively Ready issues: 2826, 2834, 2835, 2837, 2838, 2842, 2850, 2853, 2855.
    - Added the following 89 New issues: 2827, 2829, 2832, 2833, 2836, 2839, 2840, 2841, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2851, 2852, 2856, 2858, 2859, 2860, 2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2870, 2871, 2872, 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2889, 2890, 2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, 2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928.
    - Added the following 3 LEWG issues: 2831, 2854, 2857.
    - Added the following Resolved issue: 2830.
    - Added the following NAD Editorial issue: 2828.
    - Changed the following 5 issues to Tentatively Ready (from New): 2789, 2795, 2804, 2812, 2824.
    - Changed the following 2 issues to Tentatively Ready (from Open): 2768, 2769.
    - Changed the following 2 issues to Review (from New): 2790, 2796.
    - Changed the following 2 issues to LEWG (from New): 2814, 2825.
    - Changed the following 2 issues to WP (from New): 2792, 2793.
    - Changed the following 3 issues to Resolved (from New): 2805, 2809, 2810.
    - Changed the following issue to Resolved (from Open): 2754.
    - Changed the following issue to NAD (from New): 2822.
- R101: 2016-11-28 2016 post-Issaquah mailing
  - Summary:
    - 216 open issues, down by 44.
    - 2063 closed issues, up by 85.
    - 68 reassigned issues, up by 0.
    - 2347 issues total, up by 41.
  - Details:
    - Added the following 3 Ready issues: 2785, 2786, 2787.
    - Added the following Tentatively Ready issue: 2794.
    - Added the following 34 New issues: 2788, 2789, 2790, 2792, 2793, 2795, 2796, 2797, 2799, 2800, 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825.
    - Added the following Open issue: 2798.
    - Added the following 2 Resolved issues: 2791, 2817.
    - Changed the following 3 issues to Ready (from New): 2781, 2782, 2784.
    - Changed the following issue to Tentatively Ready (from Open): 2260.
    - Changed the following issue to Review (from New): 2697.
    - Changed the following 3 issues to Open (from Tentatively Ready): 2569, 2754, 2768.
    - Changed the following 2 issues to Open (from New): 2597, 2730.
    - Changed the following issue to LEWG (from New): 2780.
    - Changed the following issue to Pending NAD Editorial (from Open): 2178.
    - Changed the following 66 issues to WP (from Tentatively Ready): 2062, 2166, 2221, 2223, 2261, 2394, 2460, 2468, 2475, 2503, 2510, 2514, 2519, 2531, 2534, 2536, 2540, 2544, 2556, 2562, 2567, 2570, 2578, 2584, 2589, 2591, 2598, 2664, 2672, 2678, 2679, 2680, 2681, 2686, 2694, 2696, 2699, 2712, 2722, 2729, 2732, 2733, 2735, 2736, 2738, 2739, 2740, 2742, 2744, 2745, 2747, 2748, 2749, 2750, 2752, 2755, 2756, 2758, 2759, 2760, 2765, 2767, 2771, 2773, 2777, 2778.
    - Changed the following 4 issues to WP (from New): 2518, 2521, 2525, 2527.
    - Changed the following 3 issues to WP (from Open): 2568, 2588, 2770.
    - Changed the following 2 issues to Resolved (from Tentatively Ready): 2543, 2753.
    - Changed the following issue to Resolved (from New): 2763.
    - Changed the following issue to Resolved (from Open): 2465.
    - Changed the following issue to Resolved (from SG1): 2445.
    - Changed the following issue to NAD Editorial (from New): 2201.
    - Changed the following issue to NAD (from New): 2668.
    - Changed the following 2 issues to NAD (from Open): 1173, 2199.
- R100: 2016-10-17 2016 pre-Issaquah mailing
  - Summary:
    - 260 open issues, up by 31.
    - 1978 closed issues, up by 5.
    - 68 reassigned issues, up by 5.
    - 2306 issues total, up by 41.
  - Details:
    - Added the following 21 Tentatively Ready issues: 2744, 2745, 2747, 2748, 2749, 2750, 2752, 2753, 2754, 2755, 2756, 2758, 2759, 2760, 2765, 2767, 2768, 2771, 2773, 2777, 2778.
    - Added the following 13 New issues: 2746, 2751, 2763, 2764, 2766, 2772, 2774, 2776, 2780, 2781, 2782, 2783, 2784.
    - Added the following 2 Open issues: 2769, 2770.
    - Added the following 2 LEWG issues: 2762, 2779.
    - Added the following Tentatively Resolved issue: 2757.
    - Added the following NAD issue: 2761.
    - Added the following Dup issue: 2775.
    - Changed the following 45 issues to Tentatively Ready (from New): 2166, 2221, 2261, 2394, 2460, 2475, 2503, 2514, 2519, 2531, 2534, 2536, 2540, 2544, 2556, 2562, 2567, 2569, 2570, 2578, 2584, 2589, 2591, 2598, 2664, 2672, 2678, 2679, 2680, 2681, 2686, 2694, 2696, 2699, 2712, 2722, 2729, 2732, 2733, 2735, 2736, 2738, 2739, 2740, 2742.
    - Changed the following 5 issues to Tentatively Ready (from Open): 2062, 2223, 2468, 2510, 2543.
    - Changed the following issue to Review (from New): 2676.
    - Changed the following issue to Review (from Open): 2245.
    - Changed the following 13 issues to Open (from New): 2158, 2358, 2381, 2465, 2512, 2530, 2532, 2568, 2587, 2588, 2665, 2682, 2708.
    - Changed the following 3 issues to LEWG (from New): 2242, 2471, 2593.
    - Changed the following 3 issues to LEWG (from Open): 2095, 2152, 2153.
    - Changed the following 6 issues to Tentatively Resolved (from New): 2343, 2501, 2502, 2548, 2663, 2677.
    - Changed the following 4 issues to Tentatively Resolved (from Open): 2294, 2370, 2424, 2505.
    - Changed the following 3 issues to Tentatively Resolved (from LEWG): 839, 1041, 2443.
    - Changed the following issue to NAD Editorial (from New): 2701.
    - Changed the following 2 issues to NAD (from New): 2161, 2535.
- R99: 2016-07-11 2016 post-Oulu mailing
  - Summary:
    - 292 open issues, down by 26.
    - 1973 closed issues, up by 51.
    - 2265 issues total, up by 25.
  - Details:
    - Added the following 16 New issues: 2722, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, 2741, 2742, 2743.
    - Added the following 9 WP issues: 2719, 2720, 2721, 2723, 2724, 2725, 2726, 2727, 2728.
    - Changed the following issue to Tentatively NAD (from New): 2717.
    - Changed the following issue to Tentatively Resolved (from Open): 2241.
    - Changed the following 21 issues to WP (from Ready): 2181, 2309, 2310, 2328, 2393, 2426, 2436, 2441, 2451, 2516, 2542, 2549, 2550, 2551, 2555, 2573, 2667, 2669, 2670, 2671, 2673.
    - Changed the following 12 issues to WP (from Tentatively Ready): 2509, 2596, 2674, 2683, 2684, 2685, 2688, 2689, 2698, 2706, 2707, 2710.
    - Changed the following 6 issues to WP (from New): 2687, 2704, 2709, 2711, 2716, 2718.
    - Changed the following 2 issues to WP (from Open): 2312, 2422.
    - Changed the following issue to NAD (from New): 2700.
- R98: 2016-05-29 2016 pre-Oulu mailing
  - Summary:
    - 318 open issues, up by 50.
    - 1922 closed issues, up by 0.
    - 2240 issues total, up by 50.
  - Details:
    - Added the following 10 Tentatively Ready issues: 2596, 2683, 2684, 2685, 2688, 2689, 2698, 2706, 2707, 2710.
    - Added the following Tentatively NAD issue: 2692.
    - Added the following 37 New issues: 2595, 2597, 2598, 2599, 2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2686, 2687, 2691, 2693, 2694, 2695, 2696, 2697, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2708, 2709, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718.
    - Added the following 2 LEWG issues: 2600, 2690.
    - Changed the following issue to Tentatively Ready (from New): 2674.
    - Changed the following issue to Tentatively Ready (from Open): 2509.
    - Changed the following 2 issues to Tentatively Resolved (from New): 2208, 2529.
    - Changed the following issue to Tentatively Resolved (from Open): 2179.
    - Changed the following issue to Tentatively Resolved (from LEWG): 2391.
- R97: 2016-03-22 2016 post-Jacksonville mailing
  - Summary:
    - 268 open issues, down by 37.
    - 1922 closed issues, up by 40.
    - 2190 issues total, up by 3.
  - Details:
    - Added the following 3 New issues: 2592, 2593, 2594.
    - Changed the following 7 issues to Ready (from Review): 2181, 2309, 2310, 2328, 2393, 2441, 2516.
    - Changed the following 11 issues to Ready (from New): 2542, 2549, 2550, 2551, 2555, 2573, 2667, 2669, 2670, 2671, 2673.
    - Changed the following issue to Ready (from Open): 2426.
    - Changed the following 2 issues to Ready (from LEWG): 2436, 2451.
    - Changed the following issue to Open (from Review): 2424.
    - Changed the following issue to Open (from New): 2368.
    - Changed the following 3 issues to WP (from Ready): 2276, 2523, 2537.
    - Changed the following 25 issues to WP (from Tentatively Ready): 2192, 2450, 2520, 2522, 2539, 2545, 2557, 2558, 2559, 2560, 2565, 2566, 2571, 2572, 2574, 2575, 2576, 2577, 2579, 2581, 2582, 2583, 2585, 2586, 2590.
    - Changed the following issue to WP (from Review): 2296.
    - Changed the following issue to Resolved (from New): 2554.
    - Changed the following issue to Resolved (from Open): 2456.
    - Changed the following issue to NAD Editorial (from New): 2666.
    - Changed the following issue to NAD (from Review): 2402.
    - Changed the following issue to NAD (from New): 2553.
    - Changed the following issue to NAD (from LEWG): 2372.
    - Changed the following 2 issues to NAD Arrays (from Ready): 2253, 2255.
    - Changed the following 3 issues to NAD Arrays (from Open): 2254, 2264, 2277.
- R96: 2016-02-12 2016 pre-Jacksonville mailing (includes the FS TS bugs for the first time)
  - Summary:
    - 305 open issues, up by 48.
    - 1882 closed issues, up by 63.
    - 2187 issues total, up by 111.
  - Details:
    - Added the following 19 Tentatively Ready issues: 2557, 2558, 2559, 2560, 2565, 2566, 2571, 2572, 2574, 2575, 2576, 2577, 2579, 2581, 2582, 2583, 2585, 2586, 2590.
    - Added the following 30 New issues: 2554, 2555, 2556, 2561, 2562, 2563, 2564, 2567, 2568, 2569, 2570, 2573, 2578, 2584, 2587, 2588, 2589, 2591, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674.
    - Added the following 3 NAD Future issues: 2611, 2612, 2654.
    - Added the following 41 WP issues: 2601, 2602, 2603, 2605, 2606, 2607, 2608, 2609, 2614, 2615, 2616, 2618, 2619, 2621, 2622, 2624, 2625, 2627, 2629, 2632, 2633, 2634, 2635, 2636, 2637, 2640, 2641, 2644, 2645, 2647, 2648, 2649, 2650, 2652, 2653, 2655, 2656, 2657, 2658, 2660, 2662.
    - Added the following 2 NAD Editorial issues: 2639, 2659.
    - Added the following 14 NAD issues: 2580, 2604, 2610, 2613, 2617, 2623, 2626, 2628, 2630, 2631, 2638, 2642, 2646, 2661.
    - Added the following 2 Dup issues: 2643, 2651.
    - Changed the following issue to Tentatively Ready (from New): 2545.
    - Changed the following 2 issues to Review (from Open): 2310, 2516.
    - Changed the following issue to NAD (from New): 2552.
- R95: 2015-11-15 2015 post-Kona mailing
  - Summary:
    - 257 open issues, down by 37.
    - 1819 closed issues, up by 47.
    - 2076 issues total, up by 10.
  - Details:
    - Added the following 10 New issues: 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553.
    - Changed the following 2 issues to Ready (from New): 2523, 2537.
    - Changed the following issue to Ready (from Open): 2276.
    - Changed the following 3 issues to Tentatively Ready (from New): 2520, 2522, 2539.
    - Changed the following 2 issues to Tentatively Ready (from Open): 2192, 2450.
    - Changed the following issue to Review (from Ready): 2181.
    - Changed the following 4 issues to Review (from Open): 2309, 2393, 2402, 2441.
    - Changed the following issue to Open (from Tentatively Ready): 2510.
    - Changed the following 10 issues to Open (from New): 2117, 2164, 2290, 2468, 2499, 2505, 2509, 2516, 2524, 2543.
    - Changed the following issue to SG1 (from New): 2533.
    - Changed the following 35 issues to WP (from Ready): 1169, 2072, 2101, 2111, 2119, 2127, 2133, 2156, 2218, 2219, 2244, 2250, 2259, 2336, 2353, 2367, 2380, 2384, 2385, 2435, 2447, 2462, 2466, 2469, 2473, 2476, 2477, 2483, 2484, 2485, 2486, 2487, 2489, 2492, 2494.
    - Changed the following 8 issues to WP (from Tentatively Ready): 2224, 2234, 2273, 2495, 2500, 2515, 2517, 2526.
    - Changed the following issue to Resolved (from Core): 2165.
    - Changed the following 2 issues to NAD (from New): 2474, 2538.
    - Changed the following issue to NAD (from Open): 2379.
    - Changed the following issue to NAD (from Resolved): 2319.
- R94: 2015-09-25 2015 pre-Kona mailing
  - Summary:
    - 294 open issues, up by 38.
    - 1772 closed issues, up by 2.
    - 2066 issues total, up by 40.
  - Details:
    - Added the following 4 Tentatively Ready issues: 2510, 2515, 2517, 2526.
    - Added the following 36 New issues: 2504, 2505, 2506, 2507, 2508, 2509, 2511, 2512, 2513, 2514, 2516, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543.
    - Changed the following 2 issues to Tentatively Ready (from New): 2495, 2500.
    - Changed the following 2 issues to Tentatively Ready (from Open): 2234, 2273.
    - Changed the following issue to Resolved (from Open): 2051.
    - Changed the following issue to NAD (from New): 2326.
- R93: 2014-05-22 2015 post-Lenexa mailing
  - Summary:
    - 256 open issues, down by 36.
    - 1770 closed issues, up by 48.
    - 2026 issues total, up by 12.
  - Details:
    - Added the following 2 Ready issues: 2492, 2494.
    - Added the following 10 New issues: 2493, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503.
    - Changed the following 2 issues to Ready (from Review): 2111, 2380.
    - Changed the following 20 issues to Ready (from New): 2244, 2250, 2259, 2336, 2353, 2367, 2384, 2385, 2435, 2462, 2466, 2473, 2476, 2477, 2483, 2484, 2485, 2486, 2487, 2489.
    - Changed the following 12 issues to Ready (from Open): 1169, 2072, 2101, 2119, 2127, 2133, 2156, 2181, 2218, 2219, 2447, 2469.
    - Changed the following issue to Tentatively Ready (from Open): 2224.
    - Changed the following issue to Review (from New): 2296.
    - Changed the following issue to Review (from Open): 2328.
    - Changed the following 11 issues to Open (from New): 2262, 2289, 2338, 2348, 2349, 2370, 2398, 2402, 2422, 2450, 2456.
    - Changed the following 8 issues to Open (from SG1): 2245, 2265, 2276, 2309, 2363, 2379, 2426, 2441.
    - Changed the following issue to LEWG (from New): 2372.
    - Changed the following issue to EWG (from New): 2432.
    - Changed the following issue to Deferred (from Open): 2202.
    - Changed the following 14 issues to WP (from Ready): 2160, 2168, 2364, 2403, 2406, 2411, 2425, 2427, 2428, 2433, 2434, 2438, 2439, 2440.
    - Changed the following 18 issues to WP (from Tentatively Ready): 2059, 2076, 2239, 2369, 2378, 2410, 2415, 2418, 2437, 2448, 2454, 2455, 2458, 2459, 2463, 2467, 2470, 2482.
    - Changed the following 3 issues to WP (from New): 2420, 2464, 2488.
    - Changed the following issue to WP (from Open): 2063.
    - Changed the following 2 issues to WP (from SG1): 2407, 2442.
    - Changed the following issue to Resolved (from Review): 2228.
    - Changed the following 3 issues to Resolved (from Open): 1526, 2274, 2397.
    - Changed the following 5 issues to NAD (from New): 2079, 2251, 2351, 2373, 2386.
    - Changed the following issue to NAD (from Open): 2388.
- R92: 2015-04-09 pre-Lenexa mailing
  - Summary:
    - 292 open issues, up by 33.
    - 1722 closed issues, up by 0.
    - 2014 issues total, up by 33.
  - Details:
    - Added the following 5 Tentatively Ready issues: 2459, 2463, 2467, 2470, 2482.
    - Added the following 27 New issues: 2460, 2461, 2462, 2464, 2465, 2466, 2468, 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491.
    - Added the following Open issue: 2469.
    - Changed the following issue to Tentatively Ready (from Review): 2378.
    - Changed the following 11 issues to Tentatively Ready (from New): 2076, 2239, 2369, 2410, 2415, 2418, 2437, 2448, 2454, 2455, 2458.
    - Changed the following issue to Tentatively Ready (from Open): 2059.
    - Changed the following issue to Tentatively NAD (from New): 2337.
    - Changed the following issue to Tentatively NAD (from Open): 760.
    - Changed the following 5 issues to Open (from New): 2312, 2388, 2393, 2444, 2447.
    - Changed the following 4 issues to LEWG (from New): 2391, 2417, 2436, 2451.
    - Changed the following issue to EWG (from Open): 2089.
    - Changed the following issue to Core (from New): 2452.
    - Changed the following 13 issues to SG1 (from New): 2236, 2245, 2265, 2276, 2309, 2334, 2363, 2379, 2407, 2412, 2426, 2442, 2445.
    - Changed the following issue to SG1 (from Open): 2441.
- R91: 2014-11-23 post-Urbana mailing
  - Summary:
    - 259 open issues, up by 32.
    - 1722 closed issues, down by 20.
    - 1981 issues total, up by 12.
  - Details:
    - Added the following 12 New issues: 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458.
    - Changed the following 2 issues to Ready (from Review): 2160, 2364.
    - Changed the following 11 issues to Ready (from New): 2403, 2406, 2411, 2425, 2427, 2428, 2433, 2434, 2438, 2439, 2440.
    - Changed the following issue to Ready (from Open): 2168.
    - Changed the following issue to Review (from New): 2424.
    - Changed the following 5 issues to Open (from New): 2307, 2310, 2383, 2414, 2441.
    - Changed the following 2 issues to Open (from NAD Future): 760, 1173.
    - Changed the following 4 issues to LEWG (from New): 2419, 2430, 2443, 2446.
    - Changed the following 48 issues to LEWG (from NAD Future): 255, 423, 484, 523, 532, 708, 839, 851, 877, 933, 935, 936, 961, 1025, 1031, 1041, 1052, 1053, 1112, 1120, 1121, 1150, 1154, 1184, 1188, 1201, 1203, 1217, 1235, 1238, 1242, 1282, 1289, 1317, 1320, 1396, 1406, 1422, 1459, 1484, 1488, 1493, 1499, 1521, 2040, 2055, 2226, 2232.
    - Changed the following 2 issues to Pending NAD (from Tentatively NAD): 2302, 2382.
    - Changed the following 11 issues to WP (from Ready): 2016, 2170, 2340, 2354, 2377, 2396, 2399, 2400, 2401, 2404, 2408.
    - Changed the following 12 issues to WP (from Tentatively Ready): 2106, 2129, 2212, 2217, 2230, 2233, 2266, 2325, 2361, 2365, 2376, 2387.
    - Changed the following issue to Resolved (from Ready): 2319.
    - Changed the following issue to Resolved (from Review): 2118.
    - Changed the following issue to Resolved (from New): 2416.
    - Changed the following issue to Resolved (from Open): 2108.
    - Changed the following issue to NAD (from New): 2429.
- R90: 2014-10-13 pre-Urbana mailing
  - Summary:
    - 227 open issues, up by 31.
    - 1742 closed issues, up by 0.
    - 1969 issues total, up by 31.
  - Details:
    - Added the following 31 New issues: 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446.
    - No issues changed.
- R89: 2014-07-08 post-Rapperswil mailing
  - Summary:
    - 196 open issues, up by 14.
    - 1742 closed issues, up by 12.
    - 1938 issues total, up by 26.
  - Details:
    - Added the following 6 Ready issues: 2396, 2399, 2400, 2401, 2404, 2408.
    - Added the following 15 New issues: 2391, 2392, 2393, 2394, 2398, 2402, 2403, 2406, 2407, 2410, 2411, 2412, 2413, 2414, 2415.
    - Added the following Open issue: 2397.
    - Added the following 3 WP issues: 2390, 2395, 2409.
    - Added the following NAD issue: 2405.
    - Changed the following issue to Ready (from New): 2377.
    - Changed the following 2 issues to Ready (from Deferred): 2253, 2255.
    - Changed the following 2 issues to Tentatively Ready (from New): 2325, 2387.
    - Changed the following issue to Tentatively NAD (from New): 2382.
    - Changed the following 3 issues to Review (from New): 2364, 2378, 2380.
    - Changed the following 2 issues to Review (from Open): 2118, 2160.
    - Changed the following 3 issues to Open (from New): 2168, 2238, 2273.
    - Changed the following 3 issues to Open (from Deferred): 2254, 2264, 2277.
    - Changed the following 3 issues to WP (from New): 2371, 2374, 2389.
    - Changed the following 4 issues to Resolved (from Deferred): 2282, 2283, 2287, 2333.
    - Changed the following issue to NAD (from Deferred): 2305.
- R88: 2014-05-24 pre-Rapperswil mailing
  - Summary:
    - 182 open issues, up by 29.
    - 1730 closed issues, up by 0.
    - 1912 issues total, up by 29.
  - Details:
    - Added the following 3 Tentatively Ready issues: 2361, 2365, 2376.
    - Added the following 26 New issues: 2362, 2363, 2364, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389.
    - Changed the following 5 issues to Tentatively Ready (from Open): 2106, 2129, 2212, 2230, 2233.

## Issue Status

Issues reported to the LWG transition through a variety of statuses, indicating their progress towards a resolution. Typically, most issues will flow through the following stages.

New - The issue has not yet been reviewed by the LWG. Any Proposed Resolution is purely a suggestion from the issue submitter, and should not be construed as the view of LWG.

Open - The LWG has discussed the issue but is not yet ready to move the issue forward. There are several possible reasons for open status:

- Consensus may have not yet have been reached as to how to deal with the issue.
- Informal consensus may have been reached, but the LWG awaits exact Proposed Resolution wording for review.
- The LWG wishes to consult additional technical experts before proceeding.
- The issue may require further study.

A Proposed Resolution for an open issue is still not be construed as the view of LWG. Comments on the current state of discussions are often given at the end of open issues in an italic font. Such comments are for information only and should not be given undue importance.

Review - Exact wording of a Proposed Resolution is now available for review on an issue for which the LWG previously reached informal consensus.

Ready - The LWG has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full committee for further action as a Defect Report (DR).

Typically, an issue must have a proposed resolution in the currently published issues list, whose wording does not change during LWG review, to move to the Ready status.

Voting - This status should not be seen in a published issues list, but is a marker for use during meetings to indicate an issues was Ready in the pre-meeting mailing, the Proposed Resolution is correct, and the issue will be offered to the working group at the end of the current meeting to apply to the current working paper (WP) or to close in some other appropriate manner. This easily distinguishes such issues from those moving to Ready status during the meeting itself, that should not be forwarded until the next meeting. If the issue does not move forward, it should fall back to one of the other open states before the next list is published.

Immediate - This status should not be seen in a published issues list, but is a marker for use during meetings to indicate an issues was not Ready in the pre-meeting mailing, but the Proposed Resolution is correct, and the issue will be offered to the working group at the end of the current meeting to apply to the current working paper (WP) or to close in some other appropriate manner. This status is used only rarely, typically for fixes that are both small and obvious, and usually within a meeting of the expected publication of a revised standard. If the issue does not move forward, it should fall back to one of the other open states before the next list is published.

In addition, there are a few ways to categorise and issue that remains open to a resolution within the library, but is not actively being worked on.

Deferred - The LWG has discussed the issue, is not yet ready to move the issue forward, but neither does it deem the issue significant enough to delay publishing a standard or Technical Report. A typical deferred issue would be seeking to clarify wording that might be technically correct, but easily mis-read.

A Proposed Resolution for a deferred issue is still not be construed as the view of LWG. Comments on the current state of discussions are often given at the end of open issues in an italic font. Such comments are for information only and should not be given undue importance.

Core - The LWG has discussed the issue, and feels that some key part of resolving the issue is better handled by a cleanup of the language in the Core part of the standard. The issue is passed to the Core Working Group, which should ideally open a corresponding issue that can be linked from the library issue. Such issues will be revisitted after Core have made (or declined to make) any changes.

EWG - The LWG has discussed the issue, and wonder that some key part of resolving the issue is better handled by some (hopefully small) extension to the language. The issue is passed to the Evolution Working Group, which should ideally open a corresponding issue that can be linked from the library issue. Such issues will be revisitted after Evoltion have made (or declined to make) any recommendations. Positive recommendations from EWG will often mean the issue transition to Core status while we wait for some proposed new feature to land in the working paper.

LEWG - The LWG has discussed the issue, and deemd the issue is either an extension, however small, or changes the library design in some fundamental way, and so has delegated the initial work to the Library Evolution Working Group.

Ultimately, all issues should reach closure with one of the following statuses.

DR - (Defect Report) - The full WG21/PL22.16 committee has voted to forward the issue to the Project Editor to be processed as a Potential Defect Report. The Project Editor reviews the issue, and then forwards it to the WG21 Convenor, who returns it to the full committee for final disposition. This issues list accords the status of DR to all these Defect Reports regardless of where they are in that process.

WP - (Working Paper) - The proposed resolution has not been accepted as a Technical Corrigendum, but the full WG21/PL22.16 committee has voted to apply the Defect Report's Proposed Resolution to the working paper.

C++23 - (C++ Standard, as revised for 2023) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2023 revision to the C++ standard, ISO/IEC IS 14882:2024(E).

C++20 - (C++ Standard, as revised for 2020) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2020 revision to the C++ standard, ISO/IEC IS 14882:2020(E).

C++17 - (C++ Standard, as revised for 2017) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2017 revision to the C++ standard, ISO/IEC IS 14882:2017(E).

C++14 - (C++ Standard, as revised for 2014) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2014 revision to the C++ standard, ISO/IEC IS 14882:2014(E).

C++11 - (C++ Standard, as revised for 2011) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2011 revision to the C++ standard, ISO/IEC IS 14882:2011(E).

CD1 - (Committee Draft 2008) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the Fall 2008 Committee Draft.

TC1 - (Technical Corrigenda 1) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution as a Technical Corrigenda. Action on this issue is thus complete and no further action is possible under ISO rules.

TRDec - (Decimal TR defect) - The LWG has voted to accept the Defect Report's Proposed Resolution into the Decimal TR. Action on this issue is thus complete and no further action is expected.

TS - (TS - various) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into a published Technical Specification.

Resolved - The LWG has reached consensus that the issue is a defect in the Standard, but the resolution adopted to resolve the issue came via some other mechanism than this issue in the list - typically by applying a formal paper, occasionally as a side effect of consolidating several interacting issue resolutions into a single issue.

Dup - The LWG has reached consensus that the issue is a duplicate of another issue, and will not be further dealt with. A Rationale identifies the duplicated issue's issue number.

NAD - The LWG has reached consensus that the issue is not a defect in the Standard.

NAD Editorial - The LWG has reached consensus that the issue can either be handled editorially, or is handled by a paper (usually linked to in the rationale).

Tentatively - This is a status qualifier. The issue has been reviewed online, or at an unofficial meeting, but not in an official meeting, and some support has been formed for the qualified status. Tentatively qualified issues may be moved to the unqualified status and forwarded to full committee (if Ready) within the same meeting. Unlike Ready issues, Tentatively Ready issues will be reviewed in subcommittee prior to forwarding to full committee. When a status is qualified with Tentatively, the issue is still considered active.

Pending - This is a status qualifier. When prepended to a status this indicates the issue has been processed by the committee, and a decision has been made to move the issue to the associated unqualified status. However for logistical reasons the indicated outcome of the issue has not yet appeared in the latest working paper.

The following statuses have been retired, but may show up on older issues lists.

NAD Future - In addition to the regular status, the LWG believes that this issue should be revisited at the next revision of the standard. That is now an ongoing task managed by the Library Evolution Working Group, and most issues in this status were reopended with the status LEWG.

NAD Concepts - This status reflects an evolution of the language during the development of C++11, where a new feature entered the language, called concepts, that fundamentally changed the way templates would be specified and written. While this language feature was removed towards the end of the C++11 project, there is a clear intent to revisit this part of the language design. During that development, a number of issues were opened against the updated library related to use of that feature, or requesting fixes that would require explicit use of the concepts feature. All such issues have been closed with this status, and may be revisitted should this or a similar language feature return for a future standard.

NAD Arrays - This status reflects an evolution of the language during the development of C++14/17, where work on a Technical Specification, called the Arrays TS was begun. In early 2016, this work was abandoned, and the work item was officially withdrawn. During development of the TS, a number of issues were opened the features in the TS. All such issues have been closed with this status, and may be revisitted should this or a similar language feature return for a future standard.

Issues are always given the status of New when they first appear on the issues list. They may progress to Open or Review while the LWG is actively working on them. When the LWG has reached consensus on the disposition of an issue, the status will then change to Dup, NAD, or Ready as appropriate. Once the full PL22.16 committee votes to forward Ready issues to the Project Editor, they are given the status of Defect Report (DR). These in turn may become the basis for Technical Corrigenda (TC1), an updated standard (C++11, C++14), or are closed without action other than a Record of Response (Resolved) where the desired effect has already been achieved by some other process. The intent of this LWG process is that only issues which are truly defects in the Standard move to the formal ISO DR status.

## Active Issues

### 423(i). Effects of negative `streamsize` in iostreams

Section: 31 [input.output] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2018-12-09

Priority: 3

View other active issues in [input.output].

View all other issues in [input.output].

View all issues with Open status.

Discussion:

A third party test suite tries to exercise `istream::ignore(N)` with a negative value of `N` and expects that the implementation will treat `N` as if it were `0`. Our implementation asserts that (`N >= 0`) holds and aborts the test.

I can't find anything in section 27 that prohibits such values but I don't see what the effects of such calls should be, either (this applies to a number of unformatted input functions as well as some member functions of the `basic_streambuf` template).

[ 2009-07 Frankfurt ]

> This is related to LWG 255(i).

> Move to NAD Future.

[LEWG Kona 2017]

Recommend Open: We agree that we should require `N >= 0` for the selected functions

[2018-12-04 Reflector prioritization]

Set Priority to 3

Proposed resolution:

I propose that we add to each function in clause 27 that takes an argument, say `N`, of type `streamsize` a Requires clause saying that "`N >= 0`." The intent is to allow negative streamsize values in calls to `precision()` and `width()` but disallow it in calls to `streambuf::sgetn()`, `istream::ignore()`, or `ostream::write()`.

[Kona: The LWG agreed that this is probably what we want. However, we need a review to find all places where functions in clause 27 take arguments of type `streamsize` that shouldn't be allowed to go negative. Martin will do that review.]

### 484(i). Convertible to `T`

Section: 24.3.5.3 [input.iterators] Status: Open Submitter: Chris Jefferson Opened: 2004-09-16 Last modified: 2023-06-25

Priority: 3

View other active issues in [input.iterators].

View all other issues in [input.iterators].

View all issues with Open status.

Discussion:

From comp.std.c++:

I note that given an input iterator a for type `T`, then `*a` only has to be "convertable to `T`", not actually of type `T`.

Firstly, I can't seem to find an exact definition of "convertable to `T`". While I assume it is the obvious definition (an implicit conversion), I can't find an exact definition. Is there one?

Slightly more worryingly, there doesn't seem to be any restriction on the this type, other than it is "convertable to `T`". Consider two input iterators `a` and `b`. I would personally assume that most people would expect `*a==*b` would perform `T(*a)==T(*b)`, however it doesn't seem that the standard requires that, and that whatever type `*a` is (call it `U`) could have == defined on it with totally different symantics and still be a valid inputer iterator.

Is this a correct reading? When using input iterators should I write `T(*a)` all over the place to be sure that the object I'm using is the class I expect?

This is especially a nuisance for operations that are defined to be "convertible to `bool`". (This is probably allowed so that implementations could return say an `int` and avoid an unnecessary conversion. However all implementations I have seen simply return a `bool` anyway. Typical implementations of STL algorithms just write things like `while(a!=b && *a!=0)`. But strictly speaking, there are lots of types that are convertible to `T` but that also overload the appropriate operators so this doesn't behave as expected.

If we want to make code like this legal (which most people seem to expect), then we'll need to tighten up what we mean by "convertible to `T`".

[Lillehammer: The first part is NAD, since "convertible" is well-defined in core. The second part is basically about pathological overloads. It's a minor problem but a real one. So leave open for now, hope we solve it as part of iterator redesign.]

[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]

[ 2009-10 Santa Cruz: ]

> Mark as NAD Future. We agree there's an issue, but there is no proposed solution at this time and this will be solved by concepts in the future.

[2017-02 in Kona, LEWG recommends NAD]

Has been clarified by 14. By design. Ranges might make it go away. Current wording for input iterators is more constrained.

[2017-06-02 Issues Telecon]

Move to Open. This is very similar to 2962(i), possibly a duplicate.

Marshall to research

[2017-07 Toronto Thurs Issue Prioritization]

Priority 2; same as 2962(i).

Previous resolution [SUPERSEDED]

Rationale:

[ San Francisco: ]

> Solved by N2758.

[2023-06; Varna]

During LWG discussion of this issue it was decided to reduce the priority to 3.

Furthermore, the still presented "Solved by" comment has been recognized as being no longer true, since the referred to pre-C++11 concept paper wording N2758 is no longer part of the working paper.

It also has been observed, that the "convertible to `bool`" part has since been resolved by P1964 and the follow-up paper P2167.

Also LWG 3105(i) has a lot of overlap with this issue.

Proposed resolution:

### 523(i). regex case-insensitive character ranges are unimplementable as specified

Section: 28.6 [re] Status: Open Submitter: Eric Niebler Opened: 2005-07-01 Last modified: 2020-07-17

Priority: 4

View other active issues in [re].

View all other issues in [re].

View all issues with Open status.

Discussion:

A problem with TR1 regex is currently being discussed on the Boost developers list. It involves the handling of case-insensitive matching of character ranges such as [Z-a]. The proper behavior (according to the ECMAScript standard) is unimplementable given the current specification of the TR1 `regex_traits<>` class template. John Maddock, the author of the TR1 regex proposal, agrees there is a problem. The full discussion can be found at http://lists.boost.org/boost/2005/06/28850.php (first message copied below). We don't have any recommendations as yet.

-- Begin original message --

The situation of interest is described in the ECMAScript specification (ECMA-262), section 15.10.2.15:

"Even if the pattern ignores case, the case of the two ends of a range is significant in determining which characters belong to the range. Thus, for example, the pattern /[E-F]/i matches only the letters E, F, e, and f, while the pattern /[E-f]/i matches all upper and lower-case ASCII letters as well as the symbols [, \, ], ^, _, and `."

A more interesting case is what should happen when doing a case-insensitive match on a range such as [Z-a]. It should match z, Z, a, A and the symbols [, \, ], ^, _, and `. This is not what happens with Boost.Regex (it throws an exception from the regex constructor).

The tough pill to swallow is that, given the specification in TR1, I don't think there is any effective way to handle this situation. According to the spec, case-insensitivity is handled with `regex_traits<>::translate_nocase(CharT)` — two characters are equivalent if they compare equal after both are sent through the `translate_nocase` function. But I don't see any way of using this translation function to make character ranges case-insensitive. Consider the difficulty of detecting whether "z" is in the range [Z-a]. Applying the transformation to "z" has no effect (it is essentially `std::tolower`). And we're not allowed to apply the transformation to the ends of the range, because as ECMA-262 says, "the case of the two ends of a range is significant."

So AFAICT, TR1 regex is just broken, as is Boost.Regex. One possible fix is to redefine translate_nocase to return a string_type containing all the characters that should compare equal to the specified character. But this function is hard to implement for Unicode, and it doesn't play nice with the existing ctype facet. What a mess!

-- End original message --

[ John Maddock adds: ]

One small correction, I have since found that ICU's regex package does implement this correctly, using a similar mechanism to the current TR1.Regex.

Given an expression [c1-c2] that is compiled as case insensitive it:

Enumerates every character in the range c1 to c2 and converts it to it's case folded equivalent. That case folded character is then used a key to a table of equivalence classes, and each member of the class is added to the list of possible matches supported by the character-class. This second step isn't possible with our current traits class design, but isn't necessary if the input text is also converted to a case-folded equivalent on the fly.

ICU applies similar brute force mechanisms to character classes such as [[:lower:]] and [[:word:]], however these are at least cached, so the impact is less noticeable in this case.

Quick and dirty performance comparisons show that expressions such as "[X-\\x{fff0}]+" are indeed very slow to compile with ICU (about 200 times slower than a "normal" expression). For an application that uses a lot of regexes this could have a noticeable performance impact. ICU also has an advantage in that it knows the range of valid characters codes: code points outside that range are assumed not to require enumeration, as they can not be part of any equivalence class. I presume that if we want the TR1.Regex to work with arbitrarily large character sets enumeration really does become impractical.

Finally note that Unicode has:

Three cases (upper, lower and title). One to many, and many to one case transformations. Character that have context sensitive case translations - for example an uppercase sigma has two different lowercase forms - the form chosen depends on context(is it end of a word or not), a caseless match for an upper case sigma should match either of the lower case forms, which is why case folding is often approximated by tolower(toupper(c)).

Probably we need some way to enumerate character equivalence classes, including digraphs (either as a result or an input), and some way to tell whether the next character pair is a valid digraph in the current locale.

Hoping this doesn't make this even more complex that it was already,

[ Portland: Alisdair: Detect as invalid, throw an exception. Pete: Possible general problem with case insensitive ranges. ]

[ 2009-07 Frankfurt ]

> We agree that this is a problem, but we do not know the answer.

> We are going to declare this NAD until existing practice leads us in some direction.

> No objection to NAD Future.

> Move to NAD Future.

[LEWG Kona 2017]

Recommend Open: Tim Shen proposes: forbid use of case-insensitive ranges with regex traits other than `std::regex_traits<{char, wchar_t, char16_t, char32_t}>` when regex_constants::collate is specified.

[2020-07-17; Priority set to 4 in telecon]

Proposed resolution:

### 617(i). `std::array` is a sequence that doesn't satisfy the sequence requirements?

Section: 23.3.3 [array] Status: Open Submitter: Bo Persson Opened: 2006-12-30 Last modified: 2022-11-12

Priority: 3

View all other issues in [array].

View all issues with Open status.

Discussion:

The `<array>` header is given under 23.3 [sequences]. 23.3.3 [array]/paragraph 3 says:

> "Unless otherwise specified, all array operations are as described in 23.2 [container.requirements]".

However, `array` isn't mentioned at all in section 23.2 [container.requirements]. In particular, Table 82 "Sequence requirements" lists several operations (insert, erase, clear) that `std::array` does not have in 23.3.3 [array].

Also, Table 83 "Optional sequence operations" lists several operations that `std::array` does have, but array isn't mentioned.

[ 2009-07 Frankfurt ]

> The real issue seems to be different than what is described here. Non-normative text says that `std::array` is a sequence container, but there is disagreement about what that really means. There are two possible interpretations:

> 1. a sequence container is one that satisfies all sequence container requirements
> 2. a sequence container is one that satisfies some of the sequence container requirements. Any operation that the container supports is specified by one or more sequence container requirements, unless that operation is specifically singled out and defined alongside the description of the container itself.

> Move to Tentatively NAD.

[ 2009-07-15 Loïc Joly adds: ]

> The section 23.2.4 [sequence.reqmts]/1 states that array is a sequence. 23.2.4 [sequence.reqmts]/3 introduces table 83, named Sequence container requirements. This seems to me to be defining the requirements for all sequences. However, array does not follow all of this requirements (this can be read in the array specific section, for the standard is currently inconsistent).

> Proposed resolution 1 (minimal change):

> > Say that array is a container, that in addition follows only some of the sequence requirements, as described in the array section:

> > > The library provides five three basic kinds of sequence containers: `array`, `vector`, `forward_list`, `list`, and `deque`. In addition, `array` and `forward_list` follows some of the requirements of sequences, as described in their respective sections.

> Proposed resolution 2 (most descriptive description, no full wording provided):

> > Introduce the notion of a Fixed Size Sequence, with it requirement table that would be a subset of the current Sequence container. array would be the only Fixed Size Sequence (but dynarray is in the queue for TR2). Sequence requirements would now be requirements in addition to Fixed Size Sequence requirements (it is currently in addition to container).

[ 2009-07 Frankfurt: ]

> Move to NAD Editorial

[ 2009 Santa Cruz: ]

> This will require a lot of reorganization. Editor doesn't think this is really an issue, since the description of array can be considered as overriding what's specified about sequences. Move to NAD.

[2022-10-27; Hubert Tong comments and requests to reopen]

This issue appears to be unresolved (should not be NAD).

As noted in 23.3.3.1 [array.overview] paragraph 3, `array` does not meet 23.2.2.2 [container.reqmts] paragraph 10. This means that `array` does not meet the container requirements, never mind the requirements for sequence containers or contiguous containers.

However, there is wording that claims the opposite.

23.2.4 [sequence.reqmts] paragraph 1:

> In addition, `array` is provided as a sequence container which provides limited sequence operations because it has a fixed number of elements.

(Perhaps the above should be worded with "except".)

23.3.1 [sequences.general] paragraph 1:

> The headers `<array>` […] define class templates that meet the requirements for sequence containers.

23.3.3.1 [array.overview] paragraph 1:

> […] An `array` is a contiguous container (23.2.2 [container.requirements.general]).

In this comment, Casey suggests that the requirements be changed so that `array` does meet the requirements.

[Kona 2022-11-12; Set Priority to 3]

Proposed resolution:

### 1102(i). `std::vector`'s reallocation policy still unclear

Section: 23.3.13.3 [vector.capacity] Status: Open Submitter: Daniel Krügler Opened: 2009-04-20 Last modified: 2020-07-17

Priority: 3

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with Open status.

Discussion:

I have the impression that even the wording of current draft N2857 does insufficiently express the intent of `vector`'s reallocation strategy. This has produced not too old library implementations which release memory in the `clear()` function and even modern articles about C++ programming cultivate the belief that `clear` is allowed to do exactly this. A typical example is something like this:

>     const int buf_size = ...;
>     std::vector<T> buf(buf_size);
>     for (int i = 0; i < some_condition; ++i) {
>       buf.resize(buf_size);
>       write_or_read_data(buf.data());
>       buf.clear(); // Ensure that the next round get's 'zeroed' elements
>     }

where still the myth is ubiquitous that `buf` might be allowed to reallocate it's memory inside the `for` loop.

IMO the problem is due to the fact, that

1. the actual memory-reallocation stability of `std::vector` is explained in 23.3.13.3 [vector.capacity]/3 and /6 which are describing just the effects of the `reserve` function, but in many examples (like above) there is no explicit call to `reserve` involved. Further-more 23.3.13.3 [vector.capacity]/6 does only mention insertions and never mentions the consequences of erasing elements.

2. the effects clause of `std::vector`'s `erase` overloads in 23.3.13.5 [vector.modifiers]/4 is silent about capacity changes. This easily causes a misunderstanding, because the counter parting insert functions described in 23.3.13.5 [vector.modifiers]/2 explicitly say, that

   > Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.

   It requires a complex argumentation chain about four different places in the standard to provide the — possibly weak — proof that calling `clear()` also does never change the capacity of the `std::vector` container. Since `std::vector` is the de-facto replacement of C99's dynamic arrays this type is near to a built-in type and it's specification should be clear enough that usual programmers can trust their own reading.

[ Batavia (2009-05): ]

> Bill believes paragraph 1 of the proposed resolution is unnecessary because it is already implied (even if tortuously) by the current wording.

> Move to Review.

[ 2009-10 Santa Cruz: ]

> Mark as NAD. Rationale: there is no consensus to clarify the standard, general consensus that the standard is correct as written.

[2020-05-08; Reopen after reflector discussions]

"correct as written" has been disputed.

[2020-07-17; Priority set to 3 in telecon]

Proposed resolution:

[ This is a minimum version. I also suggest that the wording explaining the allocation strategy of `std::vector` in 23.3.13.3 [vector.capacity]/3 and /6 is moved into a separate sub paragraph of 23.3.13.3 [vector.capacity] before any of the prototype's are discussed, but I cannot provide reasonable wording changes now. ]

1. Change 23.3.13.3 [vector.capacity]/6 as follows:

   > It is guaranteed that no reallocation takes place during insertions or erasures that happen after a call to `reserve()` until the time when an insertion would make the size of the vector greater than the value of `capacity()`.

2. Change 23.3.13.5 [vector.modifiers]/4 as follows:

   > Effects: The capacity shall remain unchanged and no reallocation shall happen. Invalidates iterators and references at or after the point of the erase.

### 1175(i). `unordered` complexity

Section: 23.2.8 [unord.req] Status: Open Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2020-09-06

Priority: 3

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Open status.

Discussion:

When I look at the `unordered_*` constructors, I think the complexity is poorly described and does not follow the style of the rest of the standard.

The complexity for the default constructor is specified as constant. Actually, it is proportional to `n`, but there are no invocations of `value_type` constructors or other `value_type` operations.

For the iterator-based constructor the complexity should be:

> Complexity: exactly `n` calls to construct `value_type` from `InputIterator::value_type` (where `n = distance(f,l)`). The number of calls to `key_equal::operator()` is proportional to `n` in the average case and `n*n` in the worst case.

[ 2010 Rapperswil: ]

> Concern that the current wording may require O(1) where that cannot be delivered. We need to look at both the clause 23 requirements tables and the constructor description of each unordered container to be sure.

> Howard suggests NAD Editorial as we updated the container requirement tables since this issue was written.

> Daniel offers to look deeper, and hopefully produce wording addressing any outstanding concerns at the next meeting.

> Move to Open.

[2011-02-26: Daniel provides wording]

I strongly suggest to clean-up the differences between requirement tables and individual specifications. In the usual way, the most specific specifications wins, which is in this case the wrong one. In regard to the concern expressed about missing `DefaultConstructible` requirements of the value type I disagree: The function argument `n` is no size-control parameter, but only some effective capacity parameter: No elements will be value-initialized by these constructors. The necessary requirement for the value type, `EmplaceConstructible` into `*this`, is already listed in Table 103 — Unordered associative container requirements. Another part of the proposed resolution is the fact that there is an inconsistency of the complexity counting when both a range and a bucket count is involved compared to constructions where only bucket counts are provided: E.g. the construction `X a(n);` has a complexity of `n` bucket allocations, but this part of the work is omitted for `X a(i, j, n);`, even though it is considerable larger (in the average case) for `n ≫ distance(i, j)`.

[2011-03-24 Madrid meeting]

Move to deferred

[ 2011 Bloomington ]

The proposed wording looks good. Move to Review.

[2012, Kona]

Fix up some presentation issues with the wording, combining the big-O expressions into single expressions rather than the sum of two separate big-Os.

Strike "constant or linear", prefer "linear in the number of buckets". This allows for number of buckets being larger than requested `n` as well.

Default `n` to "unspecified" rather than "implementation-defined". It seems an un-necessary burden asking vendors to document a quantity that is easily determined through the public API of these classes.

Replace `distance(f,l)` with "number of elements in the range `[f,l)`"

Retain in Review with the updated wording

[2012, Portland: Move to Open]

The wording still does not call out Pablo's original concern, that the element constructor is called no more than `N` times, and that the `N` squared term applies to moves during rehash.

Inconsistent use of O(n)+O(N) vs. O(n+N), with a preference for the former.

AJM to update wording with a reference to "no more than `N` element constructor calls".

Matt concerned that calling out the O(n) requirements is noise, and dangerous noise in suggesting a precision we do not mean. The cost of constructing a bucket is very different to constructing an element of user-supplied type.

AJM notes that if there are multiple rehashes, the 'n' complexity is probably not linear.

Matt suggests back to Open, Pablo suggests potentially NAD if we keep revisitting without achieving a resolution.

Matt suggests complexity we are concerned with is the number of operations, such as constructing elements, moving nodes, and comparing/hashing keys. We are less concerned with constructing buckets, which are generally noise in this bigger picture.

[2015-01-29 Telecon]

AM: essentially correct, but do we want to complicate the spec?

HH: Pablo has given us permission to NAD it

JM: when I look at the first change in the P/R I find it mildly disturbing that the existing wording says you have a constant time constructor with a single element even if your `n` is 10^6, so I think adding this change makes people aware there might be a large cost in initializing the hash table, even though it doesn't show up in user-visible constructions.

HH: one way to avoid that problem is make the default ctor `noexcept`. Then the container isn't allowed to create an arbitrarily large hash table

AM: but this is the constructor where the user provides `n`

MC: happy with the changes, except I agree with the editorial recommendation to keep the two 𝒪s separate.

JW: yes, the constant '`k`' is different in 𝒪(n) and 𝒪(N)

GR: do we want to talk about buckets at all

JM: yes, good to highlight that bucket construction might be a significant cost

HH: suggest we take the suggestion to split 𝒪(n+N) to 𝒪(n)+𝒪(N) and move to Tentatively Ready

GR: 23.2.1p2 says all complexity requirements are stated solely in terms of the number of operations on the contained object, so we shouldn't be stating complexity in terms of the hash table initialization

HH: channeling Pete, there's an implicit "unless otherwise specified" everywhere.

VV: seem to be requesting modifications that render this not Tentatively Ready

GR: I think it can't be T/R

AM: make the editorial recommendation, consider fixing 23.2.1/3 to give us permission to state complexity in terms of bucket initialization

HH: only set it to Review after we get new wording to review

[2015-02 Cologne]

Update wording, revisit later.

Previous resolution [SUPERSEDED]:

> 1. Modify the following rows in Table 103 — Unordered associative container requirements to add the explicit bucket allocation overhead of some constructions. As editorial recommendation it is suggested not to shorten the sum `𝒪(n) + 𝒪(N)` to `𝒪(n + N)`, because two different work units are involved.

>    > Table 103 — Unordered associative container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition | Complexity
>    > --- | --- | --- | ---
>    > …
>    > `X(i, j, n, hf, eq)` `X a(i, j, n, hf, eq)` | `X` | … Effects: Constructs an empty container with at least `n` buckets, using `hf` as the hash function and `eq` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n + N`) (`N` is `distance(i, j)`), worst case 𝒪(`n`) + 𝒪(`N2`)
>    > `X(i, j, n, hf)` `X a(i, j, n, hf)` | `X` | … Effects: Constructs an empty container with at least `n` buckets, using `hf` as the hash function and `key_equal()` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n + N`) (`N` is `distance(i, j)`), worst case 𝒪(`n + N2`)
>    > `X(i, j, n)` `X a(i, j, n)` | `X` | … Effects: Constructs an empty container with at least `n` buckets, using `hasher()` as the hash function and `key_equal()` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n + N`) (`N` is `distance(i, j)`), worst case 𝒪(`n + N2`)
>    > …

> 2. Modify 23.5.3.2 [unord.map.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

>    >     explicit unordered_map(size_type n = see below,
>    >                            const hasher& hf = hasher(),
>    >                            const key_equal& eql = key_equal(),
>    >                            const allocator_type& a = allocator_type());

>    > > 1 Effects: Constructs an empty `unordered_map` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_map`. `max_load_factor()` returns `1.0`.

>    > > 2 Complexity: ConstantLinear in the number of buckets.

>    >     template <class InputIterator>
>    >     unordered_map(InputIterator f, InputIterator l,
>    >                   size_type n = see below,
>    >                   const hasher& hf = hasher(),
>    >                   const key_equal& eql = key_equal(),
>    >                   const allocator_type& a = allocator_type());

>    > > 3 Effects: Constructs an empty `unordered_map` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_map`. Then inserts elements from the range `[f, l)`. `max_load_factor()` returns `1.0`.

>    > > 4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in `N` and in the worst case quadratic in `N` to insert the elements, where `N` is equal to number of elements in the range `[f,l)`.

> 3. Modify 23.5.4.2 [unord.multimap.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

>    >     explicit unordered_multimap(size_type n = see below,
>    >                                 const hasher& hf = hasher(),
>    >                                 const key_equal& eql = key_equal(),
>    >                                 const allocator_type& a = allocator_type());

>    > > 1 Effects: Constructs an empty `unordered_multimap` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_multimap`. `max_load_factor()` returns `1.0`.

>    > > 2 Complexity: ConstantLinear in the number of buckets.

>    >     template <class InputIterator>
>    >     unordered_multimap(InputIterator f, InputIterator l,
>    >                        size_type n = see below,
>    >                        const hasher& hf = hasher(),
>    >                        const key_equal& eql = key_equal(),
>    >                        const allocator_type& a = allocator_type());

>    > > 3 Effects: Constructs an empty `unordered_multimap` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_multimap`. Then inserts elements from the range `[f, l)`. `max_load_factor()` returns `1.0`.

>    > > 4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in `N` and in the worst case quadratic in `N` to insert the elements, where `N` is equal to number of elements in the range `[f,l)`.

> 4. Modify 23.5.6.2 [unord.set.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

>    >     explicit unordered_set(size_type n = see below,
>    >                            const hasher& hf = hasher(),
>    >                            const key_equal& eql = key_equal(),
>    >                            const allocator_type& a = allocator_type());

>    > > 1 Effects: Constructs an empty `unordered_set` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_set`. `max_load_factor()` returns `1.0`.

>    > > 2 Complexity: ConstantLinear in the number of buckets.

>    >     template <class InputIterator>
>    >     unordered_set(InputIterator f, InputIterator l,
>    >                   size_type n = see below,
>    >                   const hasher& hf = hasher(),
>    >                   const key_equal& eql = key_equal(),
>    >                   const allocator_type& a = allocator_type());

>    > > 3 Effects: Constructs an empty `unordered_set` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_set`. Then inserts elements from the range `[f, l)`. `max_load_factor()` returns `1.0`.

>    > > 4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in `N` and in the worst case quadratic in `N` to insert the elements, where `N` is equal to number of elements in the range `[f,l)`.

> 5. Modify 23.5.7.2 [unord.multiset.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

>    >     explicit unordered_multiset(size_type n = see below,
>    >                                 const hasher& hf = hasher(),
>    >                                 const key_equal& eql = key_equal(),
>    >                                 const allocator_type& a = allocator_type());

>    > > 1 Effects: Constructs an empty `unordered_multiset` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_multiset`. `max_load_factor()` returns `1.0`.

>    > > 2 Complexity: ConstantLinear in the number of buckets.

>    >     template <class InputIterator>
>    >     unordered_multiset(InputIterator f, InputIterator l,
>    >                        size_type n = see below,
>    >                        const hasher& hf = hasher(),
>    >                        const key_equal& eql = key_equal(),
>    >                        const allocator_type& a = allocator_type());

>    > > 3 Effects: Constructs an empty `unordered_multiset` using the specified hash function, key equality function, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in `unordered_multiset`. Then inserts elements from the range `[f, l)`. `max_load_factor()` returns `1.0`.

>    > > 4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in `N` and in the worst case quadratic in `N` to insert the elements, where `N` is equal to number of elements in the range `[f,l)`.

[2019-03-17; Daniel comments and provides revised wording]

The updated wording ensures that we can now specify complexity requirements for containers even when they are not expressed in terms of the number on the contained objects by an exception of the rule. This allows us to say that `𝒪(n)` describes the complexity in terms of bucket initialization instead.

Proposed resolution:

This wording is relative to N4810.

1. Modify 23.2.2 [container.requirements.general] as indicated:

   > -2- Unless otherwise specified,All of the complexity requirements in this Clause are stated solely in terms of the number of operations on the contained objects. [Example: The copy constructor of type `vector<vector<int>>` has linear complexity, even though the complexity of copying each contained `vector<int>` is itself linear. — end example]

2. Modify 23.2.8 [unord.req] as indicated:

   > -11- In Table 70:

   > 1. (11.1) — […]

   > 2. […]

   > 3. (11.23) — […]

   > 4. (11.?) — Notwithstanding the complexity requirements restrictions of 23.2.2 [container.requirements.general], the complexity form `𝒪(n)` describes the number of operations on buckets.

3. Modify the following rows in Table 70 — "Unordered associative container requirements" to add the explicit bucket allocation overhead of some constructions.

   > [Drafting note: It is kindly suggested to the Project Editor not to shorten the sum `𝒪(n) + 𝒪(N)` to `𝒪(n + N)`, because two different work units are involved. — end drafting note]

   > Table 70 — Unordered associative container requirements (in addition to container)
   > Expression | Return type | Assertion/note pre-/post-condition | Complexity
   > --- | --- | --- | ---
   > …
   > `X()` `X a;` | `X` | Expects: […] Effects: Constructs an empty container with an unspecified number `n` of buckets, using `hasher()` as the hash function and `key_equal()` as the key equality predicate. | constant𝒪(`n`)
   > `X(i, j, n, hf, eq)` `X a(i, j, n, hf, eq)` | `X` | Expects: […] Effects: Constructs an empty container with at least `n` buckets, using `hf` as the hash function and `eq` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n`) + 𝒪(`N`) (`N` is `distance(i, j)`), worst case 𝒪(`n`) + 𝒪(`N2`)
   > `X(i, j, n, hf)` `X a(i, j, n, hf)` | `X` | Expects: […] Effects: Constructs an empty container with at least `n` buckets, using `hf` as the hash function and `key_equal()` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n`) + 𝒪(`N`) (`N` is `distance(i, j)`), worst case 𝒪(`n`) + 𝒪(`N2`)
   > `X(i, j, n)` `X a(i, j, n)` | `X` | Expects: […] Effects: Constructs an empty container with at least `n` buckets, using `hasher()` as the hash function and `key_equal()` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n`) + 𝒪(`N`) (`N` is `distance(i, j)`), worst case 𝒪(`n`) + 𝒪(`N2`)
   > `X(i, j)` `X a(i, j)` | `X` | Expects: […] Effects: Constructs an empty container with an unspecified number `n` of buckets, using `hasher()` as the hash function and `key_equal()` as the key equality predicate, and inserts elements from `[i, j)` into it. | Average case 𝒪(`n`) + 𝒪(`N`) (`N` is `distance(i, j)`), worst case 𝒪(`n`) + 𝒪(`N2`)
   > …

4. Modify 23.5.3.1 [unord.map.overview], class template `unordered_map`, as indicated:

   >     // 23.5.3.2 [unord.map.cnstr], construct/copy/destroy
   >     […]
   >     template <class InputIterator>
   >       unordered_map(InputIterator f, InputIterator l,
   >                     size_type n = see belowunspecified,
   >                     const hasher& hf = hasher(),
   >                     const key_equal& eql = key_equal(),
   >                     const allocator_type& a = allocator_type());
   >     […]
   >     unordered_map(initializer_list<value_type> il,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());
   >     […]

5. Modify 23.5.3.2 [unord.map.cnstr] as indicated:

   >     unordered_map() : unordered_map(size_type(see belowunspecified)) { }
   >     explicit unordered_map(size_type n,
   >                            const hasher& hf = hasher(),
   >                            const key_equal& eql = key_equal(),
   >                            const allocator_type& a = allocator_type());

   > > -1- Effects: Constructs an empty `unordered_map` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. For the default constructor, the number of buckets is implementation-defined. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -2- Complexity: ConstantLinear in the number of buckets.

   >     template <class InputIterator>
   >     unordered_map(InputIterator f, InputIterator l,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());
   >     unordered_map(initializer_list<value_type> il,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());

   > > -3- Effects: Constructs an empty `unordered_map` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is implementation-defined. Then inserts elements from the range `[f, l)` for the first form, or from the range `[il.begin(), il.end())` for the second form. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -4- Complexity: Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(`N`) (average case) or 𝒪(`N2`) (worst case) where `N` is the number of insertions.

6. Modify 23.5.4.1 [unord.multimap.overview], class template `unordered_multimap`, as indicated:

   >     // 23.5.4.2 [unord.multimap.cnstr], construct/copy/destroy
   >     […]
   >     template <class InputIterator>
   >       unordered_multimap(InputIterator f, InputIterator l,
   >                          size_type n = see belowunspecified,
   >                          const hasher& hf = hasher(),
   >                          const key_equal& eql = key_equal(),
   >                          const allocator_type& a = allocator_type());
   >     […]
   >     unordered_multimap(initializer_list<value_type> il,
   >                        size_type n = see belowunspecified,
   >                        const hasher& hf = hasher(),
   >                        const key_equal& eql = key_equal(),
   >                        const allocator_type& a = allocator_type());
   >     […]

7. Modify 23.5.4.2 [unord.multimap.cnstr] as indicated:

   >     unordered_multimap() : unordered_multimap(size_type(see belowunspecified)) { }
   >     explicit unordered_multimap(size_type n,
   >                                 const hasher& hf = hasher(),
   >                                 const key_equal& eql = key_equal(),
   >                                 const allocator_type& a = allocator_type());

   > > -1- Effects: Constructs an empty `unordered_multimap` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. For the default constructor, the number of buckets is implementation-defined. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -2- Complexity: ConstantLinear in the number of buckets.

   >     template <class InputIterator>
   >     unordered_multimap(InputIterator f, InputIterator l,
   >                        size_type n = see belowunspecified,
   >                        const hasher& hf = hasher(),
   >                        const key_equal& eql = key_equal(),
   >                        const allocator_type& a = allocator_type());
   >     unordered_multimap(initializer_list<value_type> il,
   >                        size_type n = see belowunspecified,
   >                        const hasher& hf = hasher(),
   >                        const key_equal& eql = key_equal(),
   >                        const allocator_type& a = allocator_type());

   > > -3- Effects: Constructs an empty `unordered_multimap` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is implementation-defined. Then inserts elements from the range `[f, l)` for the first form, or from the range `[il.begin(), il.end())` for the second form. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -4- Complexity: Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(`N`) (average case) or 𝒪(`N2`) (worst case) where `N` is the number of insertions.

8. Modify 23.5.6.1 [unord.set.overview], class template `unordered_set`, as indicated:

   >     // 23.5.6.2 [unord.set.cnstr], construct/copy/destroy
   >     […]
   >     template <class InputIterator>
   >       unordered_set(InputIterator f, InputIterator l,
   >                     size_type n = see belowunspecified,
   >                     const hasher& hf = hasher(),
   >                     const key_equal& eql = key_equal(),
   >                     const allocator_type& a = allocator_type());
   >     […]
   >     unordered_set(initializer_list<value_type> il,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());
   >     […]

9. Modify 23.5.6.2 [unord.set.cnstr] as indicated:

   >     unordered_set() : unordered_set(size_type(see belowunspecified)) { }
   >     explicit unordered_set(size_type n,
   >                            const hasher& hf = hasher(),
   >                            const key_equal& eql = key_equal(),
   >                            const allocator_type& a = allocator_type());

   > > -1- Effects: Constructs an empty `unordered_set` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. For the default constructor, the number of buckets is implementation-defined. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -2- Complexity: ConstantLinear in the number of buckets.

   >     template <class InputIterator>
   >     unordered_set(InputIterator f, InputIterator l,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());
   >     unordered_set(initializer_list<value_type> il,
   >                   size_type n = see belowunspecified,
   >                   const hasher& hf = hasher(),
   >                   const key_equal& eql = key_equal(),
   >                   const allocator_type& a = allocator_type());

   > > -3- Effects: Constructs an empty `unordered_set` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is implementation-defined. Then inserts elements from the range `[f, l)` for the first form, or from the range `[il.begin(), il.end())` for the second form. `max_load_factor()` returns `1.0`.

   > > -?- Ensures: `max_load_factor() == 1.0`

   > > -4- Complexity: Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(`N`) (average case) or 𝒪(`N2`) (worst case) where `N` is the number of insertions.

10. Modify 23.5.6.1 [unord.set.overview], class template `unordered_multiset`, as indicated:

    >     // 23.5.7.2 [unord.multiset.cnstr], construct/copy/destroy
    >     […]
    >     template <class InputIterator>
    >       unordered_multiset(InputIterator f, InputIterator l,
    >                          size_type n = see belowunspecified,
    >                          const hasher& hf = hasher(),
    >                          const key_equal& eql = key_equal(),
    >                          const allocator_type& a = allocator_type());
    >     […]
    >     unordered_multiset(initializer_list<value_type> il,
    >                        size_type n = see belowunspecified,
    >                        const hasher& hf = hasher(),
    >                        const key_equal& eql = key_equal(),
    >                        const allocator_type& a = allocator_type());
    >     […]

11. Modify 23.5.7.2 [unord.multiset.cnstr] as indicated:

    >     unordered_multiset() : unordered_multiset(size_type(see belowunspecified)) { }
    >     explicit unordered_multiset(size_type n,
    >                                 const hasher& hf = hasher(),
    >                                 const key_equal& eql = key_equal(),
    >                                 const allocator_type& a = allocator_type());

    > > -1- Effects: Constructs an empty `unordered_multiset` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. For the default constructor, the number of buckets is implementation-defined. `max_load_factor()` returns `1.0`.

    > > -?- Ensures: `max_load_factor() == 1.0`

    > > -2- Complexity: ConstantLinear in the number of buckets.

    >     template <class InputIterator>
    >     unordered_multiset(InputIterator f, InputIterator l,
    >                        size_type n = see belowunspecified,
    >                        const hasher& hf = hasher(),
    >                        const key_equal& eql = key_equal(),
    >                        const allocator_type& a = allocator_type());
    >     unordered_multiset(initializer_list<value_type> il,
    >                        size_type n = see belowunspecified,
    >                        const hasher& hf = hasher(),
    >                        const key_equal& eql = key_equal(),
    >                        const allocator_type& a = allocator_type());

    > > -3- Effects: Constructs an empty `unordered_multiset` using the specified hash function, key equality predicate, and allocator, and using at least `n` buckets. If `n` is not provided, the number of buckets is implementation-defined. Then inserts elements from the range `[f, l)` for the first form, or from the range `[il.begin(), il.end())` for the second form. `max_load_factor()` returns `1.0`.

    > > -?- Ensures: `max_load_factor() == 1.0`

    > > -4- Complexity: Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(`N`) (average case) or 𝒪(`N2`) (worst case) where `N` is the number of insertions.

### 1213(i). Meaning of valid and singular iterator underspecified

Section: 24.3 [iterator.requirements] Status: Open Submitter: Daniel Krügler Opened: 2009-09-19 Last modified: 2025-03-13

Priority: 4

View all other issues in [iterator.requirements].

View all issues with Open status.

Discussion:

The terms valid iterator and singular aren't properly defined. The fuzziness of those terms became even worse after the resolution of 208(i) (including further updates by 278(i)). In 24.3 [iterator.requirements] as of N2723 the standard says now:

> 5 - These values are called past-the-end values. Values of an iterator `i` for which the expression `*i` is defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable. Iterators can also have singular values that are not associated with any container. [...] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a non-singular value to an iterator that holds a singular value. [...] Dereferenceable values are always non-singular.

> 10 - An invalid iterator is an iterator that may be singular.

First, issue 208(i) intentionally removed the earlier constraint that past-the-end values are always non-singular. The reason for this was to support null pointers as past-the-end iterators of e.g. empty sequences. But there seem to exist different views on what a singular (iterator) value is. E.g. according to the SGI definition a null pointer is not a singular value:

> Dereferenceable iterators are always nonsingular, but the converse is not true. For example, a null pointer is nonsingular (there are well defined operations involving null pointers) even thought it is not dereferenceable.

and proceeds:

> An iterator is valid if it is dereferenceable or past-the-end.

Even if the standard prefers a different meaning of singular here, the change was incomplete, because by restricting feasible expressions of singular iterators to destruction and assignment isn't sufficient for a past-the-end iterator: Of-course it must still be equality-comparable and in general be a readable value.

Second, the standard doesn't clearly say whether a past-the-end value is a valid iterator or not. E.g. 26.11 [specialized.algorithms]/1 says:

> In all of the following algorithms, the formal template parameter `ForwardIterator` is required to satisfy the requirements of a forward iterator (24.1.3) [..], and is required to have the property that no exceptions are thrown from [..], or dereference of valid iterators.

The standard should make better clear what "singular pointer" and "valid iterator" means. The fact that the meaning of a valid value has a core language meaning doesn't imply that for an iterator concept the term "valid iterator" has the same meaning.

Let me add a final example: In 99 [allocator.concepts.members] of N2914 we find:

>     pointer X::allocate(size_type n);

> > 11 Returns: a pointer to the allocated memory. [Note: if `n == 0`, the return value is unspecified. —end note]

> [..]

>     void X::deallocate(pointer p, size_type n);

> > Preconditions: `p` shall be a non-singular pointer value obtained from a call to `allocate()` on this allocator or one that compares equal to it.

If singular pointer value would include null pointers this make the preconditions unclear if the pointer value is a result of `allocate(0)`: Since the return value is unspecified, it could be a null pointer. Does that mean that programmers need to check the pointer value for a null value before calling deallocate?

[ 2010-11-09 Daniel comments: ]

A later paper is in preparation.

[ 2010 Batavia: ]

Doesn't need to be resolved for Ox

[2014-02-20 Re-open Deferred issues as Priority 4]

Consider to await the paper.

Proposed resolution:

### 1238(i). Defining algorithms taking iterator for range

Section: 26 [algorithms] Status: Open Submitter: Alisdair Meredith Opened: 2009-10-15 Last modified: 2020-09-06

Priority: 3

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with Open status.

Discussion:

The library has many algorithms that take a source range represented by a pair of iterators, and the start of some second sequence given by a single iterator. Internally, these algorithms will produce undefined behaviour if the second 'range' is not as large as the input range, but none of the algorithms spell this out in Requires clauses, and there is no catch-all wording to cover this in clause 17 or the front matter of 25.

There was an attempt to provide such wording in paper n2944 but this seems incidental to the focus of the paper, and getting the wording of this issue right seems substantially more difficult than the simple approach taken in that paper. Such wording will be removed from an updated paper, and hopefully tracked via the LWG issues list instead.

It seems there are several classes of problems here and finding wording to solve all in one paragraph could be too much. I suspect we need several overlapping requirements that should cover the desired range of behaviours.

Motivating examples:

A good initial example is the `swap_ranges` algorithm. Here there is a clear requirement that `first2` refers to the start of a valid range at least as long as the range `[first1, last1)`. n2944 tries to solve this by positing a hypothetical `last2` iterator that is implied by the signature, and requires `distance(first2,last2) < distance(first1,last1)`. This mostly works, although I am uncomfortable assuming that `last2` is clearly defined and well known without any description of how to obtain it (and I have no idea how to write that).

A second motivating example might be the `copy` algorithm. Specifically, let us image a call like:

>     copy(istream_iterator<int>(is),istream_iterator(),ostream_iterator<int>(os));

In this case, our input iterators are literally simple `InputIterators`, and the destination is a simple `OutputIterator`. In neither case am I happy referring to `std::distance`, in fact it is not possible for the `ostream_iterator` at all as it does not meet the requirements. However, any wording we provide must cover both cases. Perhaps we might deduce `last2 == ostream_iterator<int>{}`, but that might not always be valid for user-defined iterator types. I can well imagine an 'infinite range' that writes to `/dev/null` and has no meaningful `last2`.

The motivating example in n2944 is `std::equal`, and that seems to fall somewhere between the two.

Outlying examples might be `partition_copy` that takes two output iterators, and the `_n` algorithms where a range is specified by a specific number of iterations, rather than traditional iterator pair. We should also not accidentally apply inappropriate constraints to `std::rotate` which takes a third iterator that is not intended to be a separate range at all.

I suspect we want some wording similar to:

> For algorithms that operate on ranges where the end iterator of the second range is not specified, the second range shall contain at least as many elements as the first.

I don't think this quite captures the intent yet though. I am not sure if 'range' is the right term here rather than sequence. More awkwardly, I am not convinced we can describe an Output sequence such as produce by an `ostream_iterator` as "containing elements", at least not as a precondition to the call before they have been written.

Another idea was to describe require that the trailing iterator support at least distance(input range) applications of `operator++` and may be written through the same number of times if a mutable/output iterator.

We might also consider handling the case of an output range vs. an input range in separate paragraphs, if that simplifies how we describe some of these constraints.

[ 2009-11-03 Howard adds: ]

> Moved to Tentatively NAD Future after 5 positive votes on c++std-lib.

[LEWG Kona 2017]

Recommend Open: The design is clear here; we just need wording

[2019-01-20 Reflector prioritization]

Set Priority to 3

Rationale:

Does not have sufficient support at this time. May wish to reconsider for a future standard.

Proposed resolution:

### 1422(i). `vector<bool>` iterators are not random access

Section: 23.3.14 [vector.bool] Status: Open Submitter: BSI Opened: 2010-08-25 Last modified: 2020-09-06

Priority: 3

View all other issues in [vector.bool].

View all issues with Open status.

Discussion:

Addresses GB-118

`vector<bool>` iterators are not random access iterators because their reference type is a special class, and not `bool &`. All standard libary operations taking iterators should treat this iterator as if it was a random access iterator, rather than a simple input iterator.

[ Resolution proposed in ballot comment ]

Either revise the iterator requirements to support proxy iterators (restoring functionality that was lost when the Concept facility was removed) or add an extra paragraph to the `vector<bool>` specification requiring the library to treat `vector<bool>` iterators as-if they were random access iterators, despite having the wrong reference type.

[ Rapperswil Review ]

The consensus at Rapperswil is that it is too late for full support for proxy iterators, but requiring the library to respect `vector<bool>` iterators as-if they were random access would be preferable to flagging this container as deliberately incompatible with standard library algorithms.

Alisdair to write the note, which may become normative Remark depending on the preferences of the project editor.

[ Post-Rapperswil Alisdair provides wording ]

Initial wording is supplied, deliberately using Note in preference to Remark although the author notes his preference for Remark. The issue of whether `iterator_traits<vector<bool>>::iterator_category` is permitted to report `random_access_iterator_tag` or must report `input_iterator_tag` is not addressed.

[ Old Proposed Resolution: ]

> Insert a new paragraph into 23.3.14 [vector.bool] between p4 and p5:

> > [Note All functions in the library that take a pair of iterators to denote a range shall treat `vector<bool>` iterators as-if they were random access iterators, even though the `reference` type is not a true reference.-- end note]

[ 2010-11 Batavia: ]

> Closed as NAD Future, because the current iterator categories cannot correctly describe `vector<bool>::iterator`. But saying that they are Random Access Iterators is also incorrect, because it is not too hard to create a corresponding test that fails. We should deal with the more general proxy iterator problem in the future, and see no benefit to take a partial workaround specific to `vector<bool>` now.

[2017-02 in Kona, LEWG recommends NAD]

D0022 Proxy Iterators for the Ranges Extensions - as much a fix as we’re going to get for vector<bool>.

[2017-06-02 Issues Telecon]

P0022 is exploring a resolution. We consider this to be fairly important issue

Move to Open, set priority to 3

Proposed resolution:

Rationale:

No consensus to make this change at this time.

### 1459(i). Overlapping evaluations are allowed

Section: 32.5.4 [atomics.order] Status: LEWG Submitter: Canada Opened: 2010-08-25 Last modified: 2025-10-21

Priority: 4

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with LEWG status.

Duplicate of: 1458

Discussion:

Addresses CA-21, GB-131

32.5.5 [atomics.lockfree] p.8 states:

> An atomic store shall only store a value that has been computed from constants and program input values by a finite sequence of program evaluations, such that each evaluation observes the values of variables as computed by the last prior assignment in the sequence.

... but 6.10.1 [intro.execution] p.13 states:

> If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. — end note ]

Overlapping executions can make it impossible to construct the sequence described in 32.5.5 [atomics.lockfree] p.8. We are not sure of the intention here and do not offer a suggestion for change, but note that 32.5.5 [atomics.lockfree] p.8 is the condition that prevents out-of-thin-air reads.

For an example, suppose we have a function invocation f(e1,e2). The evaluations of e1 and e2 can overlap. Suppose that the evaluation of e1 writes y and reads x whereas the evaluation of e2 reads y and writes x, with reads-from edges as below (all this is within a single thread).

     e1           e2
    Wrlx y--   --Wrlx x
          rf\ /rf
             X
            / \
    Rrlx x<-   ->Rrlx y

This seems like it should be allowed, but there seems to be no way to produce a sequence of evaluations with the property above.

In more detail, here the two evaluations, e1 and e2, are being executed as the arguments of a function and are consequently not sequenced-before each other. In practice we'd expect that they could overlap (as allowed by 6.10.1 [intro.execution] p.13), with the two writes taking effect before the two reads. However, if we have to construct a linear order of evaluations, as in 32.5.5 [atomics.lockfree] p.8, then the execution above is not permited. Is that really intended?

[ Resolution proposed by ballot comment ]

Please clarify.

[2011-03-09 Hans comments:]

I'm not proud of 32.5.4 [atomics.order] p9 (formerly p8), and I agree with the comments that this isn't entirely satisfactory. 32.5.4 [atomics.order] p9 was designed to preclude out-of-thin-air results for races among `memory_order_relaxed` atomics, in spite of the fact that Java experience has shown we don't really know how to do that adequately. In the long run, we probably want to revisit this.

However, in the short term, I'm still inclined to declare this NAD, for two separate reasons:

1. 6.10.1 [intro.execution] p15 states: "If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined." I think the examples presented here have undefined behavior as a result. It's not completely clear to me whether examples can be constructed that exhibit this problem, and don't have undefined behavior.

2. This comment seems to be using a different meaning of "evaluation" from what is used elsewhere in the standard. The sequence of evaluations here doesn't have to consist of full expression evaluations. They can be evaluations of operations like lvalue to rvalue conversion, or individual assignments. In particular, the reads and writes executed by `e1` and `e2` in the example could be treated as separate evaluations for purposes of producing the sequence. The definition of "sequenced before" in 6.10.1 [intro.execution] makes little sense if the term "evaluation" is restricted to any notion of complete expression. Perhaps we should add yet another note to clarify this? 32.5.4 [atomics.order] p10 probably leads to the wrong impression here.

   An alternative resolution would be to simply delete our flakey attempt at preventing out-of-thin-air reads, by removing 32.5.4 [atomics.order] p9-11, possibly adding a note that explains that we technically allow, but strongly discourage them. If we were starting this from scratch now, that would probably be my preference. But it seems like too drastic a resolution at this stage.

[2011-03-24 Madrid]

Moved to NAD Future

[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]

Proposed resolution:

### 1521(i). Requirements on internal pointer representations in containers

Section: 23.2.2 [container.requirements.general] Status: Open Submitter: Mike Spertus Opened: 2010-10-16 Last modified: 2019-01-20

Priority: 3

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with Open status.

Discussion:

Addresses US-104, US-141

The standard doesn't say that containers should use abstract pointer types internally. Both Howard and Pablo agree that this is the intent. Further, it is necessary for containers to be stored, for example, in shared memory with an interprocess allocator (the type of scenario that allocators are intended to support).

In spite of the (possible) agreement on intent, it is necessary to make this explicit:

An implementations may like to store the result of dereferencing the pointer (which is a raw reference) as an optimization, but that prevents the data structure from being put in shared memory, etc. In fact, a container could store raw references to the allocator, which would be a little weird but conforming as long as it has one by-value copy. Furthermore, pointers to locales, ctypes, etc. may be there, which also prevents the data structure from being put in shared memory, so we should make explicit that a container does not store raw pointers or references at all.

[ Pre-batavia ]

This issue is being opened as part of the response to NB comments US-104/141. See paper N3171 in the pre-Batavia mailing.

[2011-03-23 Madrid meeting]

Deferred

[ 2011 Batavia ]

This may be an issue, but it is not clear. We want to gain a few years experience with the C++11 allocator model to see if this is already implied by the existing specification.

[LEWG Kona 2017]

Status to Open: Acknowledged, need wording: (N4618 numbering) 23.2.1 container.requirements.general p8 first sentence. Replace non-normative note with requirement.

See discussion on LEWG Wiki

[2019-01-20 Reflector prioritization]

Set Priority to 3

Proposed resolution:

Add to the end of 23.2.2 [container.requirements.general] p. 8:

> [..] In all container types defined in this Clause, the member `get_allocator()` returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement. The container may not store internal objects whose types are of the form `T *` or `T &` except insofar as they are part of the item type or members.

### 2035(i). Output iterator requirements are broken

Section: 24.3.5.4 [output.iterators] Status: Open Submitter: Daniel Krügler Opened: 2011-02-27 Last modified: 2025-03-13

Priority: 3

View other active issues in [output.iterators].

View all other issues in [output.iterators].

View all issues with Open status.

Discussion:

During the Pittsburgh meeting the proposal N3066 became accepted because it fixed several severe issues related to the iterator specification. But the current working draft (N3225) does not reflect all these changes. Since I'm unaware whether every correction can be done editorial, this issue is submitted to take care of that. To give one example: All expressions of Table 108 — "Output iterator requirements" have a post-condition that the iterator is incrementable. This is impossible, because it would exclude any finite sequence that is accessed by an output iterator, such as a pointer to a C array. The N3066 wording changes did not have these effects.

[2011-03-01: Daniel comments:]

This issue has some overlap with the issue 2038(i) and I would prefer if we could solve both at one location. I suggest the following approach:

1. The terms `dereferencable` and `incrementable` could be defined in a more general way not restricted to iterators (similar to the concepts `HasDereference` and `HasPreincrement` from working draft N2914). But on the other hand, all current usages of `dereferencable` and `incrementable` are involved with types that satisfy iterator requirements. Thus, I believe that it is sufficient for C++0x to add corresponding definitions to 24.3.1 [iterator.requirements.general] and to let all previous usages of these terms refer to this sub-clause. Since the same problem occurs with the past-the-end iterator, this proposal suggest providing similar references to usages that precede its definition as well.

2. We also need to ensure that all iterator expressions get either an operational semantics in terms of others or we need to add missing pre- and post-conditions. E.g. we have the following ones without semantics:

   >     *r++ = o // output iterator
   >     *r--     // bidirectional iterator

   According to the SGI specification these correspond to

   >     { *r = o; ++r; }                         // output iterator
   >     { reference tmp = *r; --r; return tmp; } // bidirectional iterator

   respectively. Please note especially the latter expression for bidirectional iterator. It fixes a problem that we have for forward iterator as well: Both these iterator categories provide stronger guarantees than input iterator, because the result of the dereference operation is `reference`, and not only convertible to the value type (The exact form from the SGI documentation does not correctly refer to `reference`).

[2011-03-14: Daniel comments and updates the suggested wording]

In addition to the before mentioned necessary changes there is another one need, which became obvious due to issue 2042(i): `forward_list<>::before_begin()` returns an iterator value which is not dereferencable, but obviously the intention is that it should be incrementable. This leads to the conclusion that imposing dereferencable as a requirement for the expressions `++r` is wrong: We only need the iterator to be incrementable. A similar conclusion applies to the expression `--r` of bidirectional iterators.

[ 2011 Bloomington ]

Consensus this is the correct direction, but there are (potentially) missing incrementable preconditions on some table rows, and the Remarks on when an output iterator becomes dereferencable are probably better handled outside the table, in a manner similar to the way we word for input iterators.

There was some concern about redundant pre-conditions when the operational semantic is defined in terms of operations that have preconditions, and a similar level of concern over dropping such redundancies vs. applying a consistent level of redundant specification in all the iterator tables. Wording clean-up in either direction would be welcome.

[2011-08-18: Daniel adapts the proposed resolution to honor the Bloomington request]

There is only a small number of further changes suggested to get rid of superfluous requirements and essentially non-normative assertions. Operations should not have extra pre-conditions, if defined by "in-terms-of" semantics, see e.g. `a != b` or `a->m` for Table 107. Further, some remarks, that do not impose anything or say nothing new have been removed, because I could not find anything helpful they provide. E.g. consider the remarks for Table 108 for the operations dereference-assignment and preincrement: They don't provide additional information say nothing surprising. With the new pre-conditions and post-conditions it is implied what the remarks intend to say.

[ 2011-11-03: Some observations from Alexander Stepanov via c++std-lib-31405 ]

The following sentence is dropped from the standard section on OutputIterators:

"In particular, the following two conditions should hold: first, any iterator value should be assigned through before it is incremented (this is, for an output iterator `i, i++; i++;` is not a valid code sequence); second, any value of an output iterator may have at most one active copy at any given time (for example, `i = j; *++i = a; *j = b;` is not a valid code sequence)."

[ 2011-11-04: Daniel comments and improves the wording ]

In regard to the first part of the comment, the intention of the newly proposed wording was to make clear that for the expression

>     *r = o

we have the precondition dereferenceable and the post-condition incrementable. And for the expression

>     ++r

we have the precondition incrementable and the post-condition dereferenceable or past-the-end. This should not allow for a sequence like `i++; i++;` but I agree that it doesn't exactly say that.

In regard to the second point: To make this point clearer, I suggest to add a similar additional wording as we already have for input iterator to the "Assertion/note" column of the expression `++r`:

"Post: any copies of the previous value of `r` are no longer required to be dereferenceable or incrementable."

The proposed has been updated to honor the observations of Alexander Stepanov.

[2015-02 Cologne]

The matter is complicated, Daniel volunteers to write a paper.

Proposed resolution:

1. Add a reference to 24.3.1 [iterator.requirements.general] to the following parts of the library preceding Clause 24 Iterators library: (I stopped from 23.2.8 [unord.req] on, because the remaining references are the concrete containers)

   1. 16.4.4.3 [swappable.requirements] p5:

      > -5- A type `X` satisfying any of the iterator requirements (24.2) is `ValueSwappable` if, for any dereferenceable (24.3.1 [iterator.requirements.general]) object `x` of type `X`, `*x` is swappable.

   2. 16.4.4.6 [allocator.requirements], Table 27 — "Descriptive variable definitions", row with the expression `c`:

      > a dereferenceable (24.3.1 [iterator.requirements.general]) pointer of type `C*`

   3. 20.2.3.3 [pointer.traits.functions]:

      > Returns: The first template function returns a dereferenceable (24.3.1 [iterator.requirements.general]) pointer to `r` obtained by calling `Ptr::pointer_to(r)`; […]

   4. 27.4.3.4 [string.iterators] p. 2:

      > Returns: An iterator which is the past-the-end value (24.3.1 [iterator.requirements.general]).

   5. 28.3.4.6.2.3 [locale.time.get.virtuals] p. 11:

      >     iter_type do_get(iter_type s, iter_type end, ios_base& f,
      >       ios_base::iostate& err, tm *t, char format, char modifier) const;

      > > Requires: `t` shall be dereferenceable (24.3.1 [iterator.requirements.general]).

   6. 23.2.2 [container.requirements.general] p. 6:

      > […] `end()` returns an iterator which is the past-the-end (24.3.1 [iterator.requirements.general]) value for the container. […]

   7. 23.2.4 [sequence.reqmts] p. 3:

      > […] `q` denotes a valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterator to `a`, […]

   8. 23.2.7 [associative.reqmts] p. 8 (I omit intentionally one further reference in the same sub-clause):

      > […] `q` denotes a valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterator to `a`, […]

   9. 23.2.8 [unord.req] p. 10 (I omit intentionally one further reference in the same sub-clause):

      > […] `q` and `q1` are valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterators to `a`, […]

2. Edit 24.3.1 [iterator.requirements.general] p. 5 as indicated (The intent is to properly define incrementable and to ensure some further library guarantee related to past-the-end iterator values):

   > -5- Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. These values are called past-the-end values. Values of an iterator `i` for which the expression `*i` is defined are called dereferenceable. Values of an iterator `i` for which the expression `++i` is defined are called incrementable. The library never assumes that past-the-end values are dereferenceable or incrementable. Iterators can also have singular values that are not associated with any sequence. […]

3. Modify the column contents of Table 106 — "Iterator requirements", 24.3.5.2 [iterator.iterators], as indicated:

   > Table 106 — Iterator requirements
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `*r` | `reference` | `` | pre: `r` is dereferenceable.
   > `++r` | `X&` | `` | pre: `r` is incrementable.

4. Modify the column contents of Table 107 — "Input iterator requirements", 24.3.5.3 [input.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely `*a` and `++r`, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specification — end rationale]:

   > Table 107 — Input iterator requirements (in addition to Iterator)
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `a != b` | contextually convertible to `bool` | `!(a == b)` | pre: `(a, b)` is in the domain of `==`.
   > `*a` | convertible to `T` | `` | pre: `a` is dereferenceable. The expression `(void)*a, *a` is equivalent to `*a`. If `a == b` and `(a,b)` is in the domain of `==` then `*a` is equivalent to `*b`.
   > `a->m` | `` | `(*a).m` | pre: `a` is dereferenceable.
   > `++r` | `X&` | `` | pre: `r` is dereferenceableincrementable. post: `r` is dereferenceable or `r` is past-the-end. post: any copies of the previous value of `r` are no longer required either to be dereferenceable, incrementable, or to be in the domain of `==`.
   > `(void)r++` | `` | `(void)++r` | equivalent to `(void)++r`
   > `*r++` | convertible to `T` | `{ T tmp = *r; ++r; return tmp; }` | ``

5. Modify the column contents of Table 108 — "Output iterator requirements", 24.3.5.4 [output.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely `*r = o` and `++r`, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specification — end rationale]:

   > Table 108 — Output iterator requirements (in addition to Iterator)
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `*r = o` | result is not used | `` | pre: `r` is dereferenceable. Remark: After this operation `r` is not required to be dereferenceable and any copies of the previous value of `r` are no longer required to be dereferenceable or incrementable. post: `r` is incrementable.
   > `++r` | `X&` | `` | pre: `r` is incrementable. `&r == &++r`. Remark: After this operation `r` is not required to be dereferenceable. Remark: After this operation `r` is not required to be incrementable and any copies of the previous value of `r` are no longer required to be dereferenceable or incrementable. post: `r` is dereferenceable or `r` is past-the-endincrementable.
   > `r++` | convertible to `const X&` | `{ X tmp = r; ++r; return tmp; }` | Remark: After this operation `r` is not required to be dereferenceable. post: `r` is incrementable.
   > `*r++ = o` | result is not used | `{ *r = o; ++r; }` | Remark: After this operation `r` is not required to be dereferenceable. post: `r` is incrementable.

6. Modify the column contents of Table 109 — "Forward iterator requirements", 24.3.5.5 [forward.iterators], as indicated [Rationale: Since the return type of the expression `*r++` is now guaranteed to be type `reference`, the implied operational semantics from input iterator based on value copies is wrong — end rationale]

   > Table 109 — Forward iterator requirements (in addition to input iterator)
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `r++` | convertible to `const X&` | `{ X tmp = r; ++r; return tmp; }` | ``
   > `*r++` | reference | `{ reference tmp = *r; ++r; return tmp; }` | ``

7. Modify the column contents of Table 110 — "Bidirectional iterator requirements", 24.3.5.6 [bidirectional.iterators], as indicated:

   > Table 110 — Bidirectional iterator requirements (in addition to forward iterator)
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `--r` | `X&` | `` | pre: there exists `s` such that `r == ++s`. post: `r` is dereferenceableincrementable. `--(++r) == r`. `--r == --s` implies `r == s`. `&r == &--r`.
   > `r--` | convertible to `const X&` | `{ X tmp = r; --r; return tmp; }` | ``
   > `*r--` | reference | `{ reference tmp = *r; --r; return tmp; }` | ``

### 2038(i). Missing definition for `incrementable` iterator

Section: 24.3.5.4 [output.iterators] Status: Open Submitter: Pete Becker Opened: 2011-02-27 Last modified: 2016-01-28

Priority: 3

View other active issues in [output.iterators].

View all other issues in [output.iterators].

View all issues with Open status.

Discussion:

In comp.lang.c++, Vicente Botet raises the following questions:

> "In "24.2.4 Output iterators" there are 3 uses of incrementable. I've not found the definition. Could some one point me where it is defined?

> Something similar occurs with dereferenceable. While the definition is given in "24.2.1 In general" it is used several times before.

> Shouldn't these definitions be moved to some previous section?"

He's right: both terms are used without being properly defined.

There is no definition of "incrementable".

While there is a definition of "dereferenceable", it is, in fact, a definition of "dereferenceable iterator". "dereferenceable" is used throughout Clause 23 (Containers) before its definition in Clause 24. In almost all cases it's referring to iterators, but in 16.4.4.3 [swappable.requirements] there is a mention of "dereferenceable object"; in 16.4.4.6 [allocator.requirements] the table of Descriptive variable definitions refers to a "dereferenceable pointer"; 20.2.3.3 [pointer.traits.functions] refers to a "dereferenceable pointer"; in 28.3.4.6.2.3 [locale.time.get.virtuals]/11 (`do_get`) there is a requirement that a pointer "shall be dereferenceable". In those specific cases it is not defined.

[2011-03-02: Daniel comments:]

I believe that the currently proposed resolution of issue 2035(i) solves this issue as well.

[ 2011 Bloomington ]

Agree with Daniel, this will be handled by the resolution of 2035(i).

Proposed resolution:

### 2077(i). Further incomplete constraints for type traits

Section: 21.3.5.4 [meta.unary.prop] Status: Open Submitter: Daniel Krügler Opened: 2011-08-20 Last modified: 2016-01-28

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with Open status.

Discussion:

The currently agreed on proposed wording for 2015(i) using `remove_all_extents<T>::type` instead of the "an array of unknown bound" terminology in the precondition should be extended to some further entries especially in Table 49, notably the `is_*constructible`, `is_*assignable`, and `is_*destructible` entries. To prevent ODR violations, incomplete element types of arrays must be excluded for value-initialization and destruction for example. Construction and assignment has to be honored, when we have array-to-pointer conversions or pointer conversions of incomplete pointees in effect.

[2012, Kona]

The issue is that in three type traits, we are accidentally saying that in certain circumstances the type must give a specified answer when given an incomplete type. (Specifically: an array of unknown bound of incomplete type.) The issue asserts that there's an ODR violation, since the trait returns false in that case but might return a different version when the trait is completed.

Howard argues: no, there is no risk of an ODR violation. `is_constructible<A[]>` must return `false` regardless of whether `A` is complete, so there's no reason to forbid an array of unknown bound of incomplete types. Same argument applies to `is_assignable`. General agreement with Howard's reasoning.

There may be a real issue for `is_destructible`. None of us are sure what `is_destructible` is supposed to mean for an array of unknown bound (regardless of whether its type is complete), and the standard doesn't make it clear. The middle column doesn't say what it's supposed to do for incomplete types.

In at least one implementation, `is_destructible<A[]>` does return `true` if `A` is complete, which would result in ODR violation unless we forbid it for incomplete types.

Move to open. We believe there is no issue for `is_constructible` or `is_assignable`, but that there is a real issue for `is_destructible`.

Proposed resolution:

### 2115(i). Undefined behaviour for `valarray` assignments with `mask_array` index?

Section: 29.6.8 [template.mask.array] Status: Open Submitter: Thomas Plum Opened: 2011-12-10 Last modified: 2016-01-28

Priority: 4

View all issues with Open status.

Discussion:

Recently I received a Service Request (SR) alleging that one of our testcases causes an undefined behavior. The complaint is that 29.6.8 [template.mask.array] in C++11 (and the corresponding subclause in C++03) are interpreted by some people to require that in an assignment "`a[mask] = b`", the subscript `mask` and the rhs `b` must have the same number of elements.

IMHO, if that is the intended requirement, it should be stated explicitly.

In any event, there is a tiny editorial cleanup that could be made:

In C++11, 29.6.8.1 [template.mask.array.overview] para 2 mentions

> "the expression `a[mask] = b;`"

but the semicolon cannot be part of an expression. The correction could omit the semicolon, or change the word "expression" to "assignment" or "statement".

Here is the text of the SR, slightly modified for publication:

> Subject: SR01174 LVS _26322Y31 has undefined behavior [open]

> [Client:]
> The test case t263.dir/_26322Y31.cpp seems to be illegal as it has an undefined behaviour. I searched into the SRs but found SRs were not related to the topic explained in this mail (SR00324, SR00595, SR00838).

> >     const char vl[] = {"abcdefghijklmnopqrstuvwxyz"};
> >     const char vu[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
> >     const std::valarray<char> v0(vl, 27), vm5(vu, 5), vm6(vu, 6);
> >     std::valarray<char> x = v0;
> >     […]
> >     const bool vb[] = {false, false, true, true, false, true};
> >     const std::valarray<bool> vmask(vb, 6);
> >     x = v0;
> >     x[vmask] = vm5;      // ***** HERE....
> >     steq(&x[0], "abABeCghijklmnopqrstuvwxyz");
> >     x2 = x[vmask];       // ***** ....AND HERE
> >     […]

> This problem has already been discussed between [experts]: See thread http://gcc.gnu.org/ml/libstdc++/2009-11/threads.html#00051 Conclusion http://gcc.gnu.org/ml/libstdc++/2009-11/msg00099.html

> [Plum Hall:]
> Before I log this as an SR, I need to check one detail with you.

> I did read the email thread you mentioned, and I did find a citation (see INCITS ISO/IEC 14882-2003 Section 26.3.2.6 on valarray computed assignments):

> Quote: "If the array and the argument array do not have the same length, the behavior is undefined",

> But this applies to computed assignment (`*=`, `+=`, etc), not to simple assignment. Here is the C++03 citation re simple assignment:

> 26.3.2.2 valarray assignment [lib.valarray.assign]

> >     valarray<T>& operator=(const valarray<T>&);

> > > 1 Each element of the `*this` array is assigned the value of the corresponding element of the argument array. The resulting behavior is undefined if the length of the argument array is not equal to the length of the `*this` array.

> In the new C++11 (N3291), we find ...

> 26.6.2.3 valarray assignment [valarray.assign]

> >     valarray<T>& operator=(const valarray<T>& v);

> > > 1 Each element of the `*this` array is assigned the value of the corresponding element of the argument array. If the length of `v` is not equal to the length of `*this`, resizes `*this` to make the two arrays the same length, as if by calling `resize(v.size())`, before performing the assignment.

> So it looks like the testcase might be valid for C++11 but not for C++03; what do you think?

> [Client:]
> I quite agree with you but the two problems I mentioned:

> >     x[vmask] = vm5;      // ***** HERE....
> >     […]
> >     x2 = x[vmask];       // ***** ....AND HERE

> refer to `mask_array` assignment hence target the C++03 26.3.8 paragraph. Correct?

> [Plum Hall:]
> I mentioned the contrast between C++03 26.3.2.2 para 1 versus C++11 26.6.2.3 para 1.

> But in C++03 26.3.8, I don't find any corresponding restriction. Could you quote the specific requirement you're writing about?

> [Client:]
> I do notice the difference between c++03 26.3.2.2 and c++11 26.6.2.3 about assignments between different sized `valarray` and I perfectly agree with you.

> But, as already stated, this is not a simple `valarray` assignment but a `mask_array` assignment (c++03 26.3.8 / c++11 26.6.8). See c++11 quote below:

> 26.6.8 Class template mask_array
> 26.6.8.1 Class template mask_array overview
> [....]

> 1. This template is a helper template used by the mask subscript operator: `mask_array<T> valarray<T>::operator[](const valarray<bool>&)`.

> 2. It has reference semantics to a subset of an array specified by a boolean mask. Thus, the expression `a[mask] = b;` has the effect of assigning the elements of `b` to the masked elements in `a` (those for which the corresponding element in `mask` is true.)

> 26.6.8.2 mask_array assignment

> >     void operator=(const valarray<T>&) const;
> >     const mask_array& operator=(const mask_array&) const;

> > > 1 These assignment operators have reference semantics, assigning the values of the argument array elements to selected elements of the `valarray<T>` object to which it refers.

> In particular, [one of the WG21 experts] insisted on the piece "the elements of `b`".

> That is why I reported the test t263.dir/_26322Y31.cpp having an undefined behaviour.

> [Plum Hall:]
> OK, I can see that I will have to ask WG21; I will file an appropriate issue with the Library subgroup. In the meantime, I will mark this testcase as "DISPUTED" so that it is not required for conformance testing, until we get a definitive opinion.

[2012, Kona]

Moved to Open.

There appears to be a real need for clarification in the standard, and implementations differ in their current interpretation. This will need some research by implementers and a proposed resolution before further discussion is likely to be fruitful.

Proposed resolution:

### 2116(i). `is_nothrow_constructible` and destructors

Section: 21.3.5.4 [meta.unary.prop] Status: Open Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2023-05-25

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with Open status.

Discussion:

IMO if we specified `is_[nothrow_]constructible` in terms of a variable declaration whose validity requires destructibility, it is clearly a bug in our specification and a failure to realize the actual original intent. The specification should have been in terms of placement-new.

Daniel:
At the time of the specification this was intended and the solution is not done by removing the destruction semantics of `is_constructible`.

The design of `is_constructible` was also impacted by the previous `Constructible` concept that explicitly contained destruction semantics, because during conceptification of the library it turned out to simplify the constraints in the library because you did not need to add `Destructible` all the time. It often was implied but never spoken out in C++03.

Pure construction semantics was considered as useful as well, so `HasConstructor` did also exist and would surely be useful as trait as well.

Another example that is often overlooked: This also affects wrapper types like `pair`, `tuple`, `array` that contain potentially more than one type: This is easy to understand if you think of `T1` having a deleted destructor and `T2` having a constructor that may throw: Obviously the compiler has potentially need to use the `destructor` of `T1` in the constructor of `std::pair<T1, T2>` to ensure that the core language requirements are satisfied (All previous fully constructed sub-objects must be destructed).

The core language also honors this fact in [class.copy] p11:

> A defaulted copy/move constructor for a class `X` is defined as deleted (9.6.3 [dcl.fct.def.delete]) if `X` has:
> […]
> — any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
> […]

Dave:
This is about `is_nothrow_constructible` in particular. The fact that it is foiled by not having a `noexcept` dtor is a defect.

[2012, Kona]

Move to Open.

`is_nothrow_constructible` is defined in terms of `is_constructible`, which is defined by looking at a hypothetical variable and asking whether the variable definition is known not to throw exceptions. The issue claims that this also examines the type's destructor, given the context, and thus will return `false` if the destructor can potentially throw. At least one implementation (Howard's) does return `false` if the constructor is `noexcept(true)` and the destructor is `noexcept(false)`. So that's not a strained interpretation. The issue is asking for this to be defined in terms of placement `new`, instead of in terms of a temporary object, to make it clearer that `is_nothrow_constructible` looks at the `noexcept` status of only the constructor, and not the destructor.

Sketch of what the wording would look like:

require `is_constructible`, and then also require that a placement `new` operation does not throw. (Remembering the title of this issue... What does this imply for `swap`?

If we accept this resolution, do we need any changes to `swap`?

STL argues: no, because you are already forbidden from passing anything with a throwing destructor to `swap`.

Dietmar argues: no, not true. Maybe statically the destructor can conceivably throw for some values, but maybe there are some values known not to throw. In that case, it's correct to pass those values to `swap`.

[2017-01-27 Telecon]

Gave the issue a better title

This issue interacts with 2827(i)

Ville would like "an evolution group" to take a look at this issue.

[2020-08; LWG reflector]

A poll was taken to close the issue as NAD, but only gained three votes in favour (and one vote against, which was subsequently withdrawn).

[2022-03; LWG reflector]

A poll was taken to close the issue as NAD, with six votes in favour. (and one vote against, subsequently withdrawn).

"Write a paper if you want something else. These traits have well established meaning now." "Minimizing requirements is not as important a concern for standard library concepts as as minimizing the number of concepts. Requirements like 'I need to construct but not destroy an object' are niche enough that we don't need to support them."

[2022-11-30; LWG telecon]

Alisdair intends to write a paper for this.

[2023-05-25; May 2023 mailing]

Alisdair provided P2842R0.

Proposed resolution:

### 2117(i). `ios_base` manipulators should have `showgrouping/noshowgrouping`

Section: 28.3.4.3.3.3 [facet.num.put.virtuals], 31.5.2.2.2 [ios.fmtflags], 31.5.5.1 [fmtflags.manip] Status: Open Submitter: Benjamin Kosnik Opened: 2011-12-15 Last modified: 2023-02-07

Priority: 3

View other active issues in [facet.num.put.virtuals].

View all other issues in [facet.num.put.virtuals].

View all issues with Open status.

Discussion:

Iostreams should include a manipulator to toggle grouping on/off for locales that support grouped digits. This has come up repeatedly and been deferred. See LWG 826(i) for the previous attempt.

If one is using a locale that supports grouped digits, then output will always include the generated grouping characters. However, very plausible scenarios exist where one might want to output the number, un-grouped. This is similar to existing manipulators that toggle on/off the decimal point, numeric base, or positive sign.

See some user commentary here.

[21012, Kona]

Move to Open.

This is a feature request.

Walter is slightly uncomfortable with processing feature requests through the issues lists.

Alisdair says this is far from the first feature request that has come in from the issues list.

STL: The fact that you can turn off grouping on hex output is compelling.

Marshall: if we add this flag, we'll need to update tables 87-91 as well.

STL: If it has been implemented somewhere, and it works, we'd be glad to add it.

Howard: We need to say what the default is.

Alisdair sumarizes:

(1) We want clear wording that says what the effect is of turning the flag off;

(2) what the default values are, and

(3) how this fits into tables 87-90. (and 128)

[Issaquah 2014-02-10-12: Move to LEWG]

Since this issue was filed, we have grown a new working group that is better placed to handle feature requests.

We will track such issues with an LEWG status until we get feedback from the Library Evolution Working Group.

[Issaquah 2014-02-12: LEWG discussion]

Do we think this feature should exist?
SF | F | N | A | SA
2 | 4 | 1 | 0 | 0

Think about the ABI break for adding a flag. But this could be mitigated by putting the data into an iword instead of a flag.

This needs to change Stage 2 in [facet.num.put.virtuals].

Previous resolution, which needs the above corrections:

> This wording is relative to the FDIS.

> 1. Insert in 28.3.4.3.3.3 [facet.num.put.virtuals] paragraph 5:

>    > Stage 1: The first action of stage 1 is to determine a conversion specifier. The tables that describe this determination use the following local variables

>    >     fmtflags flags = str.flags() ;
>    >     fmtflags basefield = (flags & (ios_base::basefield));
>    >     fmtflags uppercase = (flags & (ios_base::uppercase));
>    >     fmtflags floatfield = (flags & (ios_base::floatfield));
>    >     fmtflags showpos = (flags & (ios_base::showpos));
>    >     fmtflags showbase = (flags & (ios_base::showbase));
>    >     fmtflags showgrouping = (flags & (ios_base::showgrouping));

> 2. Change header `<ios>` synopsis, [iostreams.base.overview] as indicated:

>    >     #include <iosfwd>

>    >     namespace std {
>    >       […]
>    >       // 27.5.6, manipulators:
>    >       […]
>    >       ios_base& showpoint     (ios_base& str);
>    >       ios_base& noshowpoint   (ios_base& str);
>    >       ios_base& showgrouping  (ios_base& str);
>    >       ios_base& noshowgrouping(ios_base& str);
>    >       ios_base& showpos       (ios_base& str);
>    >       ios_base& noshowpos     (ios_base& str);
>    >       […]
>    >     }

> 3. Change class `ios_base` synopsis, 31.5.2 [ios.base] as indicated:

>    >     namespace std {
>    >       class ios_base {
>    >       public:
>    >       class failure;
>    >         // 27.5.3.1.2 fmtflags
>    >         typedef T1 fmtflags;
>    >         […]
>    >         static constexpr fmtflags showpoint = unspecified ;
>    >         static constexpr fmtflags showgrouping = unspecified ;
>    >         static constexpr fmtflags showpos = unspecified ;
>    >         […]
>    >       };
>    >     }

> 4. Add a new entry to Table 122 — "`fmtflags` effects" as indicated:

>    Table 122 — `fmtflags` effects
>    Element | Effect(s) if set
>    --- | ---
>    `[…]`
>    `showpoint` | generates a decimal-point character unconditionally in generated floatingpoint output
>    `showgrouping` | generates grouping characters unconditionally in generated output
>    `[…]`

> 5. After [ios::fmtflags] p12 insert the following:

>    >     ios_base& showgrouping(ios_base& str);

>    > > -?- Effects: Calls `str.setf(ios_base::showgrouping)`.

>    > > -?- Returns: `str`.

>    >     ios_base& noshowgrouping(ios_base& str);

>    > > -?- Effects: Calls `str.unsetf(ios_base::showgrouping)`.

>    > > -?- Returns: `str`.

Proposed resolution:

### 2136(i). Postconditions vs. exceptions

Section: 16.3.2 [structure] Status: Ready Submitter: Jens Maurer Opened: 2012-03-08 Last modified: 2026-06-09

Priority: 3

View all issues with Ready status.

Discussion:

The front matter in clause 17 should clarify that postconditions will not hold if a standard library function exits via an exception. Postconditions or guarantees that apply when an exception is thrown (beyond the basic guarantee) are described in an "Exception safety" section.

[ 2012-10 Portland: Move to Open ]

Consensus that we do not clearly say this, and that we probably should. A likely location to describe the guarantees of postconditions could well be a new sub-clause following 99 [res.on.required] which serves the same purpose for requires clauses. However, we need such wording before we can make progress.

Also, see 2137(i) for a suggestion that we want to see a paper resolving both issues together.

[2015-05-06 Lenexa: EricWF to write paper addressing 2136 and 2137]

MC: Idea is to replace all such "If no exception" postconditions with "Exception safety" sections.

[2021-06-20; Daniel comments]

An informal editorial change suggestion has recently been made whose editorial implementation would promote the idea that the default assumption is that Postconditions: are only met if the function doesn't exit with an exception.

After analyzing all current existing Postconditions: elements the following seems to hold: Affected by this issue are only non-`noexcept` functions and mostly non-constructor functions (unless the Postconditions: element says something about the value of its arguments). Most existing Postconditions seem to be intended to apply only in non-exceptional cases. I found some where this is presumably not intended, namely those of the expressions `os << x` and `is >> v` in Tables [tab:rand.req.eng] and [tab:rand.req.dist], maybe also 30.11.2.4 [time.zone.db.remote] p4.

Nonetheless, the editorial change seems to be applicable even without having this issue resolved, because it doesn't actually change the normative state by itself.

[2024-10-03; Jonathan adds wording]

[Brno 2026-06-09; LWG issue processing]

23.6.8.7 [flat.map.modifiers] has a Postconditions: for `extract` which says "`*this` is emptied , even if the function exits via an exception". Similarly for `flat_set::extract` in 23.6.11.5 [flat.set.modifiers] and the `flat_multifoo::extract` functions.

Christian suggests new wording with "unless otherwise specified" so that the flat adaptor cases are not invalidated.

Previous resolution [SUPERSEDED]

This wording is relative to N4988.

1. Change 16.3.2.4 [structure.specifications] as indicated:

   > (3.6) — Postconditions: the conditions (sometimes termed observable results) established by the function when a call to it returns normally.

[Brno 2026-06-09; Christian provides new wording]

[Brno 2026-06-09; Change status Open → Ready]

Proposed resolution:

This wording is relative to N5046.

1. Change 16.3.2.4 [structure.specifications] as indicated:

   > (3.4) — Postconditions: the conditions (sometimes termed observable results) established by the function. Unless otherwise specified, postconditions only apply when the function returns normally.

### 2137(i). Misleadingly constrained post-condition in the presence of exceptions

Section: 28.6.7.3 [re.regex.assign] Status: Ready Submitter: Jonathan Wakely Opened: 2012-03-08 Last modified: 2026-06-09

Priority: 3

View all other issues in [re.regex.assign].

View all issues with Ready status.

Discussion:

The post-conditions of `basic_regex<>::assign` 28.6.7.3 [re.regex.assign] p16 say:

> If no exception is thrown, `flags()` returns `f` and `mark_count()` returns the number of marked sub-expressions within the expression.

The default expectation in the library is that post-conditions only hold, if there is no failure (see also 2136(i)), therefore the initial condition should be removed to prevent any misunderstanding.

[ 2012-10 Portland: Move to Open ]

A favorable resolution clearly depends on a favorable resolution to 2136(i). There is also a concern that this is just one example of where we would want to apply such a wording clean-up, and which is really needed to resolve both this issue and 2136(i) is a paper providing the clause 17 wording that gives the guarantee for postcondition paragraphs, and then reviews clauses 18-30 to apply that guarantee consistently. We do not want to pick up these issues piecemeal, as we risk opening many issues in an ongoing process.

[2015-05-06 Lenexa: EricWF to write paper addressing 2136 and 2137]

[2024-10-03; Jonathan comments]

I could find no other cases in the entire standard where we say something like this in a Postconditions: element. 31.6.3.5.4 [streambuf.virt.pback] p2 says "Postconditions: On return, the constraints of [...]" which is probably redundant (postconditions are always "on return").

[Brno 2026-06-09; Change status Open → Ready.]

Proposed resolution:

This wording is relative to N3376.

>     template <class string_traits, class A>
>       basic_regex& assign(const basic_string<charT, string_traits, A>& s,
>         flag_type f = regex_constants::ECMAScript);

> > […]

> > -15- Effects: Assigns the regular expression contained in the string `s`, interpreted according the flags specified in `f`. If an exception is thrown, `*this` is unchanged.

> > -16- Postconditions: If no exception is thrown, `flags()` returns `f` and `mark_count()` returns the number of marked sub-expressions within the expression.

### 2146(i). Are reference types `CopyConstructible`/`MoveConstructible`/`CopyAssignable`/`MoveAssignable`/`Destructible`?

Section: 16.4.4.2 [utility.arg.requirements] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-03-23 Last modified: 2024-12-04

Priority: 3

View all other issues in [utility.arg.requirements].

View all issues with Open status.

Discussion:

According to 16.4.4.2 [utility.arg.requirements] p1

> The template definitions in the C++ standard library refer to various named requirements whose details are set out in tables 17-24. In these tables, `T` is an object or reference type to be supplied by a C++ program instantiating a template; `a`, `b`, and `c` are values of type (possibly `const`) `T`; `s` and `t` are modifiable lvalues of type `T`; `u` denotes an identifier; `rv` is an rvalue of type `T`; and `v` is an lvalue of type (possibly `const`) `T` or an rvalue of type `const T`.

Is it really intended that `T` may be a reference type? If so, what should `a`, `b`, `c`, `s`, `t`, `u`, `rv`, and `v` mean? For example, are "`int &`" and "`int &&`" `MoveConstructible`?

As far as I understand, we can explicitly specify template arguments for `std::swap` and `std::for_each`. Can we use reference types there?

1. >     #include <iostream>
   >     #include <utility>

   >     int main()
   >     {
   >       int x = 1;
   >       int y = 2;
   >       std::swap<int &&>(x, y); // undefined?
   >       std::cout << x << " " << y << std::endl;
   >     }

2. >     #include <algorithm>
   >     #include <iostream>
   >     #include <iterator>
   >     #include <utility>

   >     struct F
   >     {
   >       void operator()(int n)
   >       {
   >         std::cout << n << std::endl;
   >         ++count;
   >       }
   >       int count;
   >     } f;

   >     int main()
   >     {
   >       int arr[] = { 1, 2, 3 };
   >       auto&& result = std::for_each<int *, F &&>( // undefined?
   >         std::begin(arr),
   >         std::end(arr),
   >         std::move(f));
   >       std::cout << "count: " << result.count << std::endl;
   >     }

Are these forms of usage well-defined?

Let's also consider the following constructor of `std::thread`:

>     template <class F, class ...Args>
>     explicit thread(F&& f, Args&&... args);

> > Requires: `F` and each `Ti` in `Args` shall satisfy the `MoveConstructible` requirements.

When the first argument of this constructor is an lvalue (e.g. a name of a global function), template argument for `F` is deduced to be lvalue reference type. What should "`MoveConstructible`" mean with regard to an lvalue reference type? Maybe the wording should say that `std::decay<F>::type` and each `std::decay<Ti>::type` (where `Ti` is an arbitrary item in `Args`) shall satisfy the `MoveConstructible` requirements?

[2013-03-15 Issues Teleconference]

Moved to Open.

The questions raised by the issue are real, and should have a clear answer.

[2015-10, Kona Saturday afternoon]

STL: std::thread needs to be fixed, and anything behaving like it needs to be fixed, rather than reference types. std::bind gets this right. We need to survey this. GR: That doesn't sound small to me. STL: Seach for CopyConstructible etc. It may be a long change, but not a hard one.

MC: It seems that we don't have a PR. Does anyone have one? Is anyone interested in doing a survey?

[2016-03, Jacksonville]

Casey volunteers to make a survey

[2016-06, Oulu]

During an independent survey performed by Daniel as part of the analysis of LWG 2716(i), some overlap was found between these two issues. Daniel suggested to take responsibility for surveying LWG 2146(i) and opined that the P/R of LWG 2716(i) should restrict to forwarding references, where the deduction to lvalue references can happen without providing an explicit template argument just by providing an lvalue function argument.

[2018-06, Rapperwsil]

Jonathan says that this will be covered by his Omnibus requirements paper.

[2019 Cologne Wednesday night]

Daniel will start working on this again; Marshall to provide rationale why some of the examples are not well-formed.

[2020-10-02; Issue processing telecon: change from P2 to P3]

For the examples given in the original report, the `for_each` case is now banned, because 26.2 [algorithms.requirements] p15 forbids explicit template argument lists. `std::thread`'s constructor has also been fixed to describe the requirements on `decay_t<T>` instead of `T`.

We believe we're more careful these days about using `remove_cvref` or `decay` as needed, but there are still places where we incorrectly state requirements in terms of types that might be references. The `swap` case still needs solving. Still need a survey.

[2024-03-15; LWG 4047(i) addresses the `swap` part]

[2024-12-04; Daniel comments]

The mentioned requirement sets have been renamed a while ago to:

- Cpp17CopyConstructible

- Cpp17MoveConstructible

- Cpp17CopyAssignable

- Cpp17MoveAssignable

- Cpp17Destructible

Proposed resolution:

### 2152(i). Instances of standard container types are not swappable

Section: 16.4.4.3 [swappable.requirements], 23.2.2 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2020-09-06

Priority: 3

View all other issues in [swappable.requirements].

View all issues with LEWG status.

Discussion:

Sub-clause 16.4.4.3 [swappable.requirements] defines two notions of swappability: a binary version defining when two objects are swappable with one another, and a unary notion defining whether an object is swappable (without qualification), with the latter definition requiring that the object satisfy the former with respect to all values of the same type.

Let `T` be a container type based on a non-propagating allocator whose instances do not necessarily compare equal. Then sub-clause 23.2.2 [container.requirements.general] p7 implies that no object `t` of type `T` is swappable (by the unary definition).

Throughout the standard it is the unary definition of "swappable" that is listed as a requirement (with the exceptions of 22.2.2 [utility.swap] p4, 22.3.2 [pairs.pair] p31, 22.4.4.4 [tuple.swap] p2, 26.7.3 [alg.swap] p2, and 26.7.3 [alg.swap] p6, which use the binary definition). This renders many of the mutating sequence algorithms of sub-clause 26.7 [alg.modifying.operations], for example, inapplicable to sequences of standard container types, even where every element of the sequence is swappable with every other.

Note that this concern extends beyond standard containers to all future allocator-based types.

Resolution proposal:

I see two distinct straightforward solutions:

1. Modify the requirements of algorithms from sub-clause 26.7 [alg.modifying.operations], and all other places that reference the unary "swappable" definition, to instead use the binary "swappable with" definition (over a domain appropriate to the context). The unary definition of "swappable" could then be removed from the standard.
2. Modify sub-clause 23.2.2 [container.requirements.general] such that objects of standard container types are "swappable" by the unary definition.

I favor the latter solution, for reasons detailed in the following issue.

[ 2012-10 Portland: Move to Open ]

The issue is broader than containers with stateful allocotors, although they are the most obvious example contained within the standard itself. The basic problem is that once you have a stateful allocator, that does not `propagate_on_swap`, then whether two objects of this type can be swapped with well defined behavior is a run-time property (the allocators compare equal) rather than a simple compile-time property that can be deduced from the type. Strictly speaking, any type where the nature of swap is a runtime property does not meet the `swappable` requirements of C++11, although typical sequences of such types are going to have elements that are all `swappable with` any other element in the sequence (using our other term of art for specifying requirements) as the common case is a container of elements who all share the same allocator.

The heart of the problem is that the `swappable` requirments demand that any two objects of the same type be `swappable with` each other, so if any two such objects would not be `swappable with` each other, then the whole type is never `swappable`. Many algorithms in clause 25 are specified in terms of `swappable` which is essentially an overspecification as all they actually need is that any element in the sequence is `swappable with` any other element in the sequence.

At this point Howard joins the discussion and points out that the intent of introducing the two swap-related terms was to support `vector<bool>::reference` types, and we are reading something into the wording that was never intended. Consuses is that regardless of the intent, that is what the words today say.

There is some support to see a paper reviewing the whole of clause 25 for this issue, and other select clauses as may be necessary.

There was some consideration to introducing a note into the front of clause 25 to indicate `swappable` requirements in the clause should be interpreted to allow such awkward types, but ultimately no real enthusiasm for introducing a `swappable for clause 25` requirement term, especially if it confusingly had the same name as a term used with a subtly different meaning through the rest of the standard.

There was no enthusiasm for the alternate resolution of requiring containers with unequal allocators that do not propagate provide a well-defined swap behavior, as it is not believed to be possible without giving `swap` linear complexity for such values, and even then would require adding the constraint that the container element types are CopyConstructible.

Final conclusion: move to open pending a paper from a party with a strong interest in stateful allocators.

[2016-03 Jacksonville]

Alisdair says that his paper P0178 addresses this.

[2016-06 Oulu]

P0178 reviewed, and sent back to LEWG for confirmation.

Thursday Morning: A joint LWG/LEWG meeting declined to adopt P0178.

[2017-02 in Kona, LEWG responds]

Note in the issue that this is tracked here

[2017-06-02 Issues Telecon]

Leave as LEWG; priority 3

Proposed resolution:

Apply P0178.

### 2153(i). Narrowing of the non-member `swap` contract

Section: 22.2.2 [utility.swap], 16.4.4.3 [swappable.requirements], 23.2.2 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2020-10-02

Priority: 2

View all other issues in [utility.swap].

View all issues with LEWG status.

Discussion:

Sub-clause 22.2.2 [utility.swap] defines a non-member 'swap' function with defined behavior for all `MoveConstructible` and `MoveAssignable` types. It does not guarantee constant-time complexity or `noexcept` in general, however this definition does render all objects of `MoveConstructible` and `MoveAssignable` type swappable (by the unary definition of sub-clause 16.4.4.3 [swappable.requirements]) in the absence of specializations or overloads.

The overload of the non-member `swap` function defined in Table 96, however, defines semantics incompatible with the generic non-member `swap` function, since it is defined to call a member `swap` function whose semantics are undefined for some values of `MoveConstructible` and `MoveAssignable` types.

The obvious (perhaps naive) interpretation of sub-clause 16.4.4.3 [swappable.requirements] is as a guide to the "right" semantics to provide for a non-member `swap` function (called in the context defined by 16.4.4.3 [swappable.requirements] p3) in order to provide interoperable user-defined types for generic programming. The standard container types don't follow these guidelines.

More generally, the design in the standard represents a classic example of "contract narrowing". It is entirely reasonable for the contract of a particular `swap` overload to provide more guarantees, such as constant-time execution and `noexcept`, than are provided by the `swap` that is provided for any `MoveConstructible` and `MoveAssignable` types, but it is not reasonable for such an overload to fail to live up to the guarantees it provides for general types when it is applied to more specific types. Such an overload or specialization in generic programming is akin to an override of an inherited virtual function in OO programming: violating a superclass contract in a subclass may be legal from the point of view of the language, but it is poor design and can easily lead to errors. While we cannot prevent user code from providing overloads that violate the more general `swap` contract, we can avoid doing so within the library itself.

My proposed resolution is to draw a sharp distinction between member `swap` functions, which provide optimal performance but idiosyncratic contracts, and non-member `swap` functions, which should always fulfill at least the contract of 22.2.2 [utility.swap] and thus render objects swappable. The member `swap` for containers with non-propagating allocators, for example, would offer constant-time guarantees and `noexcept` but would only offer defined behavior for values with allocators that compare equal; non-member `swap` would test allocator equality and then dispatch to either member `swap` or `std::swap` depending on the result, providing defined behavior for all values (and rendering the type "swappable"), but offering neither the constant-time nor the `noexcept` guarantees.

[2013-03-15 Issues Teleconference]

Moved to Open.

This topic deserves more attention than can be given in the telecon, and there is no proposed resolution.

[2016-03 Jacksonville]

Alisdair says that his paper P0178 addresses this.

[2016-08 Chicago]

Send to LEWG

[2016-06 Oulu]

P0178 reviewed, and sent back to LEWG for confirmation.

Thursday Morning: A joint LWG/LEWG meeting declined to adopt P0178.

[2020-10-02; remove P0178 as Proposed Resolution]

Proposed resolution:

### 2157(i). How does `std::array<T,0>` initialization work when `T` is not default-constructible?

Section: 23.3.3.5 [array.zero] Status: Open Submitter: Daryle Walker Opened: 2012-05-08 Last modified: 2021-03-14

Priority: 3

View all other issues in [array.zero].

View all issues with Open status.

Discussion:

Objects of `std::array<T, N>` are supposed to be initialized with aggregate initialization (when not the destination of a copy or move). This clearly works when `N` is positive. What happens when `N` is zero? To continue using an (inner) set of braces for initialization, a `std::array<T, 0>` implementation must have an array member of at least one element, and let default initialization take care of those secret elements. This cannot work when `T` has a set of constructors and the default constructor is deleted from that set. Solution: Add a new paragraph in 23.3.3.5 [array.zero]:

> The unspecified internal structure of array for this case shall allow initializations like:

> >     array<T, 0> a = { };

> and said initializations must be valid even when `T` is not default-constructible.

[2012, Portland: Move to Open]

Some discussion to understand the issue, which is that implementations currently have freedom to implement an empty `array` by holding a dummy element, and so might not support value initialization, which is surprising when trying to construct an empty container. However, this is not mandated, it is an unspecified implementation detail.

Jeffrey points out that the implication of 23.3.3.1 [array.overview] is that this initialization syntax must be supported by empty `array` objects already. This is a surprising inference that was not obvious to the room, but consensus is that the reading is accurate, so the proposed resolution is not necessary, although the increased clarity may be useful.

Further observation is that the same clause effectively implies that `T` must always be DefaultConstructible, regardless of `N` for the same reasons - as an initializer-list may not supply enough values, and the remaining elements must all be value initialized.

Concern that we are dancing angels on the head of pin, and that relying on such subtle implications in wording is not helpful. We need a clarification of the text in this area, and await wording.

[2015-02 Cologne]

DK: What was the outcome of Portland? AM: Initially we thought we already had the intended behaviour. We concluded that `T` must always be `DefaultConstructible`, but I'm not sure why. GR: It's p2 in `std::array`, "up to `N`". AM: That wording already implies that "`{}`" has to work when `N` is zero. But the wording of p2 needs to be fixed to make clear that it does not imply that `T` must be `DefaultConstructible`.

Conclusion: Update wording, revisit later.

[2015-10, Kona Saturday afternoon]

MC: How important is this? Can you not just use default construction for empty arrays?

TK: It needs to degenerate properly from a pack. STL agrees.

JW: Yes, this is important, and we have to make it work.

MC: I hate the words "initialization like".

JW: I'll reword this.

WEB: Can I ask that once JW has reworded this we move it to Review rather than Open?

MC: We'll try to review it in a telecon and hopefully get it to tentatively ready.

STL: Double braces must also work: `array<T, 0> a = {{}};`.

Jonathan to reword.

[2018-03-14 Wednesday evening issues processing]

Jens suggested that we remove the requirement that `begin() == end() ==` unique-value, specifically the unique value part.

Previous resolution [SUPERSEDED]:

> This wording is relative to N3376.

> Add the following new paragraph between the current 23.3.3.5 [array.zero] p1 and p2:

> > -1- `array` shall provide support for the special case `N == 0`.

> > -?- The unspecified internal structure of `array` for this case shall allow initializations like:

> > >     array<T, 0> a = { };

> > and said initializations must be valid even when `T` is not default-constructible.

> > -2- In the case that `N == 0`, `begin() == end() ==` unique value. The return value of `data()` is unspecified.

> > -3- The effect of calling `front()` or `back()` for a zero-sized array is undefined.

> > -4- Member function `swap()` shall have a noexcept-specification which is equivalent to `noexcept(true)`.

[2018-06-14, Jonathan Wakely comments and provides revised wording]

The new wording does not address the 2018-03-14 suggestion from Jens to remove the unique value. It wasn't clear to me that there was consensus to make that change, and it would be a change in behaviour not just a clarification of the existing wording.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4750.

> Modify 23.3.3.5 [array.zero] as indicated:

> > -1- `array` shall provides support for the special case of a zero-sized `array` that is always empty, i.e. `N == 0`, with the properties described in this subclause.

> > -?- A zero-sized `array` type is an aggregate that meets the `DefaultConstructible` (Table 22) and `CopyConstructible` (Table 24) requirements. There is a single element of the aggregate, of an unspecified `DefaultConstructible` type. [Note: This allows initialization of the form `array<T, 0> a = {{}};`. There is no requirement for `T` to be `DefaultConstructible`. — end note]

> > -2- In the case that `N == 0`, `begin() == end() ==` unique value `begin()` and `end()` return non-dereferenceable iterators such that `begin() == end()` and `a.begin() != b.begin()` where `a` and `b` are distinct objects of the same zero-sized `array` type. The return value of `data()` is unspecified.

> > -3- The effect of calling `front()` or `back()` for a zero-sized array is undefined.

> > -4- Member function `swap()` shall havehas constant complexity and a non-throwing exception specification.

[2018-08-30, Jonathan revises wording following feedback from Daniel Kruegler and Tim Song.]

Daniel noted that it's undefined to compare iterators from different containers, so `a.begin() != b.begin()` can't be used. That means whether the iterators from different containers are unique is unobservable anyway. We can say they don't share the same underlying sequence, which tells users they can't compare them and tells implementors they can't return value-initialized iterators.
Tim noted that it's not sufficient to say the unspecified type in a zero-sized array is DefaultConstructible, it also needs to be constructible from `= {}`. Also, a zero-sized array should be CopyAssignable.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4762.

> Modify 23.3.3.5 [array.zero] as indicated:

> > -1- `array` shall provides support for the special case of a zero-sized `array` that is always empty, i.e. `N == 0`, with the properties described in this subclause.

> > -?- A zero-sized `array` type is an aggregate that meets the Cpp17DefaultConstructible (Table 24) and Cpp17CopyConstructible (Table 26) and Cpp17CopyAssignable (Table 28) requirements. There is a single element of the aggregate, of an unspecified Cpp17DefaultConstructible type that is copy-list-initializable from an empty list. [Note: This allows initialization of the form `array<T, 0> a = {{}};`. There is no requirement for `T` to be Cpp17DefaultConstructible. — end note]

> > -2- In the case that `N == 0`, `begin() == end() ==` unique value `begin()` and `end()` return non-dereferenceable iterators such that `begin() == end()`. When `a` and `b` are distinct objects of the same zero-sized `array` type, `a.begin()` and `b.begin()` are not iterators over the same underlying sequence. [Note: Therefore `begin()` does not return a value-initialized iterator — end note]. The return value of `data()` is unspecified.

> > -3- The effect of calling `front()` or `back()` for a zero-sized array is undefined.

> > -4- Member function `swap()` shall havehas constant complexity and a non-throwing exception specification.

[2021-03-14; Johel Ernesto Guerrero Peña comments and provides improved wording]

The currently proposed wording specifies:

> There is a single element of the aggregate, of an unspecified Cpp17DefaultConstructible type that is copy-list-initializable from an empty list.

This doesn't specify which expressions involving zero-sized array specializations are constant expressions. 23.3.3.1 [array.overview] p4 specifies `array<T, 0>` to be a structural type when `T` is a structural type. This requires that its single element, let's call it single-element, be a structural type. But that says nothing about which of the special member functions of single-element are constant expressions. By being a structural type, single-element is permitted to be implemented as a literal class type. To meet this requirement, single-element can be implemented to have one constexpr constructor that is not a copy or move constructor (6.9.1 [basic.types.general] p10), so its default constructor needn't be constexpr. This is unlike non-zero-sized array specializations, which inherit these properties from `T`. Furthermore, this permits an implementation of single-element whose default constructor stores the result of `std::source_location::current()` in a data member (as exemplified in the specification for `current`). Cpp17DefaultConstructible doesn't require the default constructor to produce equal values. The simplest way to solve these issues and any other that might arise from future changes and oversights would be to specify single-element as an empty aggregate type. Then the wording from 23.3.3.2 [array.cons] p1 makes it clear that all the special member functions are constant expressions. It would also mean that the default constructor produces template-argument-equivalent values.

Proposed resolution:

This wording is relative to N4878.

Modify 23.3.3.5 [array.zero] as indicated:

1. > -1- `array` shall provides support for the special case of a zero-sized `array` that is always empty, i.e. `N == 0`, with the properties described in this subclause.

   > -?- A zero-sized `array` type is an aggregate that meets the Cpp17DefaultConstructible (Table 29 [tab:cpp17.defaultconstructible]) and Cpp17CopyConstructible (Table 31 [tab:cpp17.copyconstructible]) and Cpp17CopyAssignable (Table 33 [tab:cpp17.copyassignable]) requirements. There is a single element of the aggregate, of an unspecified empty aggregate type. [Note: This allows initialization of the form `array<T, 0> a = {{}};`. There is no requirement for `T` to be Cpp17DefaultConstructible. — end note]

   > -2- In the case that `N == 0`, `begin() == end() ==` unique value `begin()` and `end()` return non-dereferenceable iterators such that `begin() == end()`. When `a` and `b` are distinct objects of the same zero-sized `array` type, `a.begin()` and `b.begin()` are not iterators over the same underlying sequence. [Note: Therefore `begin()` does not return a value-initialized iterator — end note].. The return value of `data()` is unspecified.

   > -3- The effect of calling `front()` or `back()` for a zero-sized array is undefined.

   > -4- Member function `swap()` shall havehas constant complexity and a non-throwing exception specification.

### 2158(i). Conditional copy/move in `std::vector`

Section: 23.3.13.3 [vector.capacity] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-05-08 Last modified: 2022-11-06

Priority: 3

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with Open status.

Discussion:

There are various operations on `std::vector` that can cause elements of the vector to be moved from one location to another. A move operation can use either rvalue or const lvalue as argument; the choice depends on the value of `!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value`, where `T` is the element type. Thus, some operations on `std::vector` (e.g. 'resize' with single parameter, 'reserve', 'emplace_back') should have conditional requirements. For example, let's consider the requirement for 'reserve' in N3376 – 23.3.13.3 [vector.capacity]/2:

> Requires: `T` shall be `MoveInsertable` into `*this`.

This requirement is not sufficient if an implementation is free to select copy constructor when `!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value` evaluates to true. Unfortunately, `is_copy_constructible` cannot reliably determine whether `T` is really copy-constructible. A class may contain public non-deleted copy constructor whose definition does not exist or cannot be instantiated successfully (e.g., `std::vector<std::unique_ptr<int>>` has copy constructor, but this type is not copy-constructible). Thus, the actual requirements should be:

- if `!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value` then `T` shall be `CopyInsertable` into `*this`;

- otherwise `T` shall be `MoveInsertable` into `*this`.

Maybe it would be useful to introduce a new name for such conditional requirement (in addition to "`CopyInsertable`" and "`MoveInsertable`").

[2016-08 Chicago]

The problem does not appear to be as severe as described. The `MoveInsertable` requirements are consistently correct, but an issue may arise on the exception-safety guarantees when we check for `is_copy_constructible_v<T>`. The problem, as described, is typically for templates that appear to have a copy constructor, but one that fails to compile once instantiated, and so gives a misleading result for the trait.

In general, users should not provide such types, and the standard would not serve users well by trying to address support for such types. However, the standard should not be providing such types either, such as `vector<unique_ptr<T>>`. A possible resolution would be to tighten the constraints in Table 80 — Container Requirements, so that if the Requirements for the copy constructor/assingment operator of a container are not satisfied, that operation shall be deleted.

A futher problem highlighted by this approach is that there are no constraints on the copy-assignment operator, so that `vector<unique_ptr<T>>` should be `CopyAssignable`! However, we can lift the equivalent constraints from the Allocator-aware container requirements.

[08-2016, Chicago]

Fri PM: Move to Open

[2017-11 Albuquerque Saturday issues processing]

There's a bunch of uses of "shall" here that are incorrect. Also, CopyInsertable contains some semantic requirements, which can't be checked at compile time, so 'ill-formed' is not possible for detecting that.

[2018-06 Rapperswil Wednesday issues processing]

Daniel to provide updated wording.

[2018-06-12, Daniel provides revised wording]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4606.

> 23.2.2 [container.requirements.general] Table 80 — Container requirements
> Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity
> `X(a)` |  |  | Requires: `T` is `CopyInsertable` into `X` (see below)., otherwise this expression shall be ill-formed. post: `a == X(a)`. | linear
> `X u(a)` `X u = a;` |  |  | Requires: `T` is `CopyInsertable` into `X` (see below)., otherwise this expression shall be ill-formed. post: `u == a`. | linear
> ... | ... | ... | ... | ...
> `r = a` | `X&` |  | Requires: `T` is `CopyInsertable` into `X` and `CopyAssignable`, otherwise this expression shall be ill-formed. post: `r == a`. | linear

> 23.2.2 [container.requirements.general] Table 83 — Allocator-aware container requirements
> Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity
> `a = t` | `X&` |  | Requires: `T` is `CopyInsertable` into `X` and `CopyAssignable`., otherwise this expression shall be ill-formed post: `r == a`. | linear

[2018-08-23 Batavia Issues processing. Priority to 3]

Changed `CopyInsertable` -> `Cpp17CopyInsertable` in the resolution.

Tim says that the wording is not quite right — it imposes additional requirements.

[2022-11-06; Daniel comments]

This issue has considerable overlap with LWG 3758(i).

Proposed resolution:

This wording is relative to N4750.

> The revised wording below uses the new Mandates: element introduced by adopting P0788R3 at the Rapperswil meeting 2018 and which will become a new term of art with Jonathan's omnibus paper throughout the Standard Library.

23.2.2 [container.requirements.general] Table 77 — Container requirements
Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity
`X(a)` |  |  | Mandates: Syntactic requirements of `T` is `Cpp17CopyInsertable` into `X` (see below). Requires: `T` is `Cpp17CopyInsertable` into `X` (see below). post: `a == X(a)`. | linear
`X u(a)` `X u = a;` |  |  | Mandates: Syntactic requirements of `T` is `Cpp17CopyInsertable` into `X` (see below). Requires: `T` is `Cpp17CopyInsertable` into `X` (see below). post: `u == a`. | linear
... | ... | ... | ... | ...
`r = a` | `X&` |  | Mandates: Syntactic requirements of `T` is `Cpp17CopyInsertable` into `X` (see below) and `CopyAssignable`. Requires: `T` is `Cpp17CopyInsertable` into `X` and `CopyAssignable`. post: `r == a`. | linear

23.2.2 [container.requirements.general] Table 80 — Allocator-aware container requirements
Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity
`a = t` | `X&` |  | Mandates: Syntactic requirements of `T` is `Cpp17CopyInsertable` into `X` and `CopyAssignable`. Requires: `T` is `Cpp17CopyInsertable` into `X` and `CopyAssignable`. post: `r == a`. | linear

### 2173(i). The meaning of `operator +` in the description of the algorithms

Section: 26 [algorithms] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-08-01 Last modified: 2018-06-12

Priority: 4

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with Open status.

Discussion:

According to 26.1 [algorithms.general]/12,

> In the description of the algorithms operators `+` and `-` are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics of `a+n` is the same as that of

> >     X tmp = a;
> >     advance(tmp, n);
> >     return tmp;

There are several places where such operator `+` is applied to an output iterator — for example, see the description of `std::copy`:

>     template<class InputIterator, class OutputIterator>
>     OutputIterator copy(InputIterator first, InputIterator last,
>                         OutputIterator result);

> > -1- Effects: Copies elements in the range `[first,last)` into the range `[result,result + (last - first))` starting from `first` and proceeding to `last`. For each non-negative integer `n < (last - first)`, performs `*(result + n) = *(first + n)`.

`std::advance` is not supposed to be applicable to output iterators, so we need a different method of description.

See also message c++std-lib-32908.

[2014-06-07 Daniel comments and provides wording]

The specification for output iterators is somewhat tricky, because here a sequence of increments is required to be combined with intervening assignments to the dereferenced iterator. I tried to respect this fact by using a conceptual assignment operation as part of the specification.

Another problem in the provided as-if-code is the question which requirements are imposed on `n`. Unfortunately, the corresponding function `advance` is completely underspecified in this regard, so I couldn't borrow wording from it. We cannot even assume here that `n` is the difference type of the iterator, because for output iterators there is no requirements for this associated type to be defined. The presented wording attempts to minimize assumptions, but still can be considered as controversial.

[2018-06 Rapperswil Wednesday issues processing]

Status to Open

Proposed resolution:

This wording is relative to N4606.

1. Change 26.1 [algorithms.general] around p12 as indicated:

   > -12- In the description of the algorithms operators `+` and `-` are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics of `a+n` is the same as that of

   > >     X tmp = a;
   > >     advance(tmp, n);
   > >     return tmp;

   > when `X` meets the input iterator requirements (24.3.5.3 [input.iterators]), otherwise it is the same as that of

   > >     X tmp = a;
   > >     for (auto i = n; i; ++tmp, (void) --i)
   > >       *tmp = Expr(i);
   > >     return tmp;

   > where `Expr(i)` denotes the `(n-i)`th expression that is assigned to for the corresponding algorithm; and that of `b-a` is the same as of

   > >     return distance(a, b);

### 2189(i). Throwing `swap` breaks unordered containers' state

Section: 23.2.8.2 [unord.req.except] Status: Open Submitter: Alisdair Meredith Opened: 2012-09-23 Last modified: 2019-07-22

Priority: 3

View all issues with Open status.

Discussion:

The hash functor and key-comparison functor of unordered containers are allowed to throw on `swap`.

23.2.8.2 [unord.req.except]p3 "For unordered associative containers, no `swap` function throws an exception unless that exception is thrown by the swap of the container's Hash or Pred object (if any)."

In such a case we must offer the basic exception safety guarantee, where both objects are left in valid but unspecified states, and no resources are leaked. This yields a corrupt, un-usable container if the first `swap` succeeds, but the second fails by throwing, as the functors form a matched pair.

So our basic scenario is first, swap the allocators if the allocators propagate on swap, according to `allocator_traits`. Next we swap the pointers to our internal hash table data structures, so that they match the allocators that allocated them. (Typically, this operation cannot throw). Now our containers are back in a safely destructible state if an exception follows.

Next, let's say we swap the hash functor, and that throws. We have a corrupt data structure, in that the buckets are not correctly indexed by the correct functors, lookups will give unpredicatable results etc. We can safely restore a usable state by forcibly clearing each container - which does not leak resources and leaves us with two (empty but) usable containers.

Now let us assume that the hasher swap succeeds. Next we swap the equality comparator functor, and this too could throw. The important point to bear in mind is that these two functors form an important pairing - two objects that compare equal by the equality functor must also hash to the same value. If we swap one without the other, we most likely leave the container in an unusable state, even if we clear out all elements.

1. A colleague pointed out that the solution for this is to dynamically allocate the two functors, and then we need only swap pointers, which is not a throwing operation. And if we don't want to allocate on default construction (a common QoI request), we might consider moving to a dynamically allocated functors whenever `swap` is called, or on first insertion. Of course, allocating memory in `swap` is a whole new can of worms, but this does not really sound like the design we had intended.

2. The simplest option is to say that we do not support hasher or equality functors that throw on ADL `swap`. Note that the requirement is simply to not throw, rather than to be explicitly marked as `noexcept`. Throwing functors are allowed, so long as we never use values that would actually manifest a throw when used in an unordered container.

Pablo went on to give me several more options, to be sure we have a full set to consider:

3. Disallow one or the other functor from throwing. In that case, the possibly-throwing functor must be swapped first, then the other functor, the allocator, and the data pointer(s) afterwards (in any order -- there was a TC that allocator assignment and swap may not throw if the corresponding propagation trait is true.). Of course, the question becomes: which functor is allowed to throw and which one is not?

4. Require that any successful functor `swap` be reliably reversible. This is very inventive. I know of no other place in the standard where such a requirement is stated, though I have occasionally wanted such a guarantee.

5. Allow a failed swap to leave the containers in a state where future insertions may fail for reasons other than is currently allowed. Specifically, if the hash and equality functors are out of sync, all insertions will fail. Presumably some "incompletely swapped" exception would be thrown. This is "slightly" inventive, although people have been discussing "radioactive" states for a while.

[2013-03-15 Issues Teleconference]

Moved to Open.

[2019 Cologne Wednesday night]

Billy to write resolution for option #2. This may require a paper.

Proposed resolution:

### 2198(i). `max_load_factor(z)` makes no strong guarantees, but bans useful behavior

Section: 23.2.8 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-10-09 Last modified: 2016-12-10

Priority: 3

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Open status.

Discussion:

The user cannot specify a `max_load_factor` for their unordered container at construction, it must be supplied after the event, when the container is potentially not empty. The contract for this method is deliberately vague, not guaranteeing to use the value supplied by the user, and any value actually used will be used as a ceiling that the container will attempt to respect.

The only guarantee we have is that, if user requests a `max_load_factor` that is less than the current `load_factor`, then the operation will take constant time, thus outlawing an implementation that chooses to rehash and so preserve as a class invariant that `load_factor < max_load_factor`.

Reasonable options conforming to the standard include ignoring the user's request if the requested value is too low, or deferring the rehash to the next `insert` operation and allowing the container to have a strange state (wrt `max_load_factor`) until then - and there is still the question of rehashing if the next `insert` is for a duplicate key in a unique container.

Given the deliberate vagueness of the current wording, to support a range of reasonable (but not perfect) behaviors, it is not clear why the equally reasonable rehash to restore the constraint should be outlawed. It is not thought that this is a performance critical operation, where users will be repeatedly setting low load factors on populated containers, in a tight or (less unlikely) an instant response scenario.

[2013-03-15 Issues Teleconference]

Moved to Open.

Alisdair to provide wording.

[2016-11-12, Issaquah]

Sat PM: Howard to provide wording

[2016-11-17 Howard provided wording.]

> The provided wording is consistent with LWG discussion in Issaquah. An implementation of the proposed wording would be setting `max_load_factor()` to `max(z, load_factor())`. This preserves the container invariant:

> >     load_factor() <= max_load_factor()

> And it preserves the existing behavior that no rehash is done by this operation.

> If it is desired to change the `max_load_factor()` to something smaller than the current `load_factor()` that can be done by first reducing the current `load_factor()` by either increasing `bucket_count()` (via `rehash` or `reserve`), or decreasing `size()` (e.g. `erase`), and then changing `max_load_factor()`.

> This resolution reaffirms that `load_factor() <= max_load_factor()` is a container invariant which can never be violated.

[2016-11-27, Nico comments]

Current implementations behave differently.

In regard to the sentence

> "The only guarantee we have is that, if user requests a `max_load_factor` that is less than the current `load_factor`, then the operation will take constant time, thus outlawing an implementation that chooses to rehash and so preserve as a class invariant that `load_factor < max_load_factor`."

Note that the current spec says that there is constant complexity without any precondition. So, rehashing to keep the invariant would violate the spec (which is probably not be the intention).

This issue is related to LWG 2199(i).

Proposed resolution:

Modify Table 87 as follows:

Table 87 — Unordered associative container requirements
Expression | Return type | Assertion/note pre-/post-condition | Complexity
--- | --- | --- | ---
`a.max_load_factor(z)` | `void` | Pre: `z` shall be positive. May change the container's maximum load factor, uing `z` as a hint. Post: `a.load_factor() <= a.max_load_factor()` Note: `a.load_factor()` is not modified by this operation. | Constant

### 2206(i). Inaccuracy in `initializer_list` constructor requirements

Section: 23.2.4 [sequence.reqmts], 23.2.7 [associative.reqmts], 23.2.8 [unord.req], 29.5.3.2 [rand.req.seedseq] Status: Open Submitter: Jeffrey Yasskin Opened: 2012-10-21 Last modified: 2020-09-06

Priority: 3

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with Open status.

Discussion:

In 23.2.4 [sequence.reqmts] p3, we have "`il` designates an object of type `initializer_list<value_type>`", and then several functions that take '`il`' as an argument. However, an expression like `{1, 2, 'a'}` is not an object of type `initializer_list<int>` unless it's used to initialize an explicitly-typed variable of that type. I believe we want:

>     std::vector<int> v;
>     v = {1, 2, 'a'};

to compile portably, so we should say something different when defining '`il`'. The same phrasing happens in 23.2.7 [associative.reqmts], 23.2.8 [unord.req], and 29.5.3.2 [rand.req.seedseq].

This may just be an editorial issue because the actual class synopses declare the functions to take `initializer_list<exact_type>`.

[2013-03-15 Issues Teleconference]

Moved to Open.

This is definitely not NAD

Should copy the suggested wording as the proposed resolution.

[2019-03-26; Daniel comments and provides wording]

The 2013-03-15 comment is confusing, since it recommends to "copy the suggested wording as the proposed resolution". I couldn't find such wording in the issue nor in the associated wiki, so I provided that wording out of myself. The tricky part is to define which kind of braced-init-list we want to allow. As Tim Song pointed out, we still need the existing support for `std::initializer_list<value_type>` as well, because otherwise existing semantics based on expressions such as `li.begin()` won't work anymore. The below suggested wording restricts supported braced-init-lists to every initializer list that can be used to copy-list-initialize an object of type `std::initializer_list<value_type>` by saying:

> "`bil` designates any braced-init-list suitable to copy-list-initialize an object of type `initializer_list<value_type>` (9.5.5 [dcl.init.list])"

As a drive-by fix, the provided wording adds another initialization "expression" that makes the construction of the form

>     std::vector<int> v = {1, 2, 'a'};

valid (We just miss a copy-initialization case).

Proposed resolution:

This wording is relative to N4810.

> [Drafting note: We need to special-case the "expression" `X u = bil;` below, because for empty braced-init-list the effects are those of calling the default constructor. — end drafting note]

1. Modify 23.2.4 [sequence.reqmts] as indicated:

   > -3- In Tables 66 and 67, […] `il` designates an objectvalue of type `initializer_list<value_type>`, `bil` designates any braced-init-list suitable to copy-list-initialize an object of type `initializer_list<value_type>` (9.5.5 [dcl.init.list]), […]

2. Modify Table 66 — "Sequence container requirements (in addition to container)" as indicated:

   > Table 66 — Sequence container requirements (in addition to container)
   > Expression | Return type | Assertion/note pre-/post-condition
   > --- | --- | ---
   > `[…]`
   > `X(il)` `X u = il;` | `` | Equivalent to `X(il.begin(), il.end())` or `X u(il.begin(), il.end());`, respectively
   > `X(bil)` | `` | Equivalent to `X(initializer_list<value_type>(bil))`
   > `X u = bil;` | `` | If `bil` is empty, equivalent to `X u;`, otherwise equivalent to `X u = initializer_list<value_type>(bil);`
   > `a = il` | `X&` | […]
   > `a = bil` | `X&` | Equivalent to `a = initializer_list<value_type>(bil)`
   > `[…]`
   > `a.insert(p, il)` | `iterator` | […]
   > `a.insert(p, bil)` | `iterator` | Equivalent to `a.insert(p, initializer_list<value_type>(bil))`
   > `[…]`
   > `a.assign(il)` | `void` | […]
   > `a.assign(bil)` | `void` | Equivalent to `a.assign(initializer_list<value_type>(bil))`
   > `[…]`

3. Modify 23.2.7 [associative.reqmts] as indicated:

   > -8- In Table 69, […] `il` designates an objectvalue of type `initializer_list<value_type>`, `bil` designates any braced-init-list suitable to copy-list-initialize an object of type `initializer_list<value_type>` (9.5.5 [dcl.init.list]), […]

4. Modify Table 69 — "Associative container requirements (in addition to container)" as indicated:

   > Table 69 — Associative container requirements (in addition to container)
   > Expression | Return type | Assertion/note pre-/post-condition | Complexity
   > --- | --- | --- | ---
   > `[…]`
   > `X(il)` `X u = il;` | `` | same as `X(il.begin(), il.end())` or `X u(il.begin(), il.end());`, respectively | same as `X(il.begin(), il.end())` or `X u(il.begin(), il.end());`, respectively
   > `X(bil)` | `` | Equivalent to `X(initializer_list<value_type>(bil))` |
   > `X u = bil;` | `` | If `bil` is empty, equivalent to `X u;`, otherwise equivalent to `X u = initializer_list<value_type>(bil);` |
   > `X(il,c)` | `` | same as `X(il.begin(), il.end(), c)` | same as `X(il.begin(), il.end(), c)`
   > `X(bil, c)` | `` | Equivalent to `X(initializer_list<value_type>(bil), c)` |
   > `a = il` | `X&` | […] | […]
   > `a = bil` | `X&` | Equivalent to `a = initializer_list<value_type>(bil)` |
   > `[…]`
   > `a.insert(il)` | `void` | equivalent to `a.insert(il.begin(), il.end())` |
   > `a.insert(bil)` | `void` | Equivalent to `a.insert(initializer_list<value_type>(bil))` |
   > `[…]`
   > `a.assign(il)` | `void` | […]
   > `a.assign(bil)` | `void` | Equivalent to `a.assign(initializer_list<value_type>(bil))`
   > `[…]`

5. Modify 23.2.8 [unord.req] p11's bullet list as indicated:

   > -11- In Table 70:

   > 1. (11.1) — […]

   > 2. […]

   > 3. (11.14) — `il` denotes a value of type `initializer_list<value_type>`,

   > 4. (11.?) — `bil` denotes any braced-init-list suitable to copy-list-initialize an object of type `initializer_list<value_type>` (9.5.5 [dcl.init.list]),

   > 5. […]

6. Modify Table 70 — "Unordered associative container requirements (in addition to container)" as indicated:

   > [Drafting note: There is a preexisting issue with Table 70, that there is no symbol `u` specified ("`u` denotes the name of a variable being declared"), so existing initialization forms with a named variable are currently always written as "`X a[…]`" where `a` is defined as "`a` denotes a value of type `X`", the wording below follows this existing practice but the author of this wording would like to kindly ask the Project Editor to introduce said symbol `u` and apply it to all existing and new such named initialization forms instead. — end drafting note]

   > Table 70 — Unordered associative container requirements (in addition to container)
   > Expression | Return type | Assertion/note pre-/post-condition | Complexity
   > --- | --- | --- | ---
   > `[…]`
   > `X(il)` `X a = il;` | `X` | Same as `X(il.begin(), il.end())` or `X a(il.begin(), il.end());`, respectively | Same as `X(il.begin(), il.end())` or `X a(il.begin(), il.end());`, respectively
   > `X(bil)` | `X` | Equivalent to `X(initializer_list<value_type>(bil))` |
   > `X a = bil;` | `X` | If `bil` is empty, equivalent to `X a;`, otherwise equivalent to `X a = initializer_list<value_type>(bil);` |
   > `X(il, n)` | `X` | Same as `X(il.begin(), il.end(), n)` | Same as `X(il.begin(), il.end(), n)`
   > `X(bil, n)` | `X` | Equivalent to `X(initializer_list<value_type>(bil), n)` |
   > `X(il, n, hf)` | `X` | Same as `X(il.begin(), il.end(), n, hf)` | Same as `X(il.begin(), il.end(), n, hf)`
   > `X(bil, n, hf)` | `X` | Equivalent to `X(initializer_list<value_type>(bil), n, hf)` |
   > `X(il, n, hf, eq)` | `X` | Same as `X(il.begin(), il.end(), n, hf, eq)` | Same as `X(il.begin(), il.end(), n, hf, eq)`
   > `X(bil, n, hf, eq)` | `X` | Equivalent to `X(initializer_list<value_type>(bil), n, hf, eq)` |
   > `[…]`
   > `a = il` | `X&` | […] | […]
   > `a = bil` | `X&` | Equivalent to `a = initializer_list<value_type>(bil)` |
   > `[…]`
   > `a.insert(il)` | `void` | Same as `a.insert(il.begin(), il.end())`. | Same as `a.insert(il.begin(), il.end())`.
   > `a.insert(bil)` | `void` | Equivalent to `a.insert(initializer_list<value_type>(bil))` |
   > `[…]`

7. Modify 29.5.3.2 [rand.req.seedseq] p2's bullet list as indicated:

   > -2- A class `S` satisfies the requirements of a seed sequence if the expressions shown in Table 82 are valid and have the indicated semantics, and […] In that Table and throughout this subclause:

   > 1. (2.1) — […]

   > 2. (2.?) — `u` denotes the name of a variable being declared,

   > 3. […]

   > 4. (2.6) — `il` is a value of `initializer_list<T>`.;

   > 5. (2.?) — `bil` denotes any braced-init-list suitable to copy-list-initialize an object of type `initializer_list<T>` (9.5.5 [dcl.init.list]).

8. Modify Table 82 — "Seed sequence requirements" as indicated:

   > Table 82 — Seed sequence requirements
   > Expression | Return type | Pre/post-condition | Complexity
   > --- | --- | --- | ---
   > `[…]`
   > `S(il)` `S u = il;` |  | Same as `S(il.begin(), il.end())` or `S u(il.begin(), il.end());`, respectively | same as `S(il.begin(), il.end())` or `S u(il.begin(), il.end());`, respectively
   > `S(bil)` |  | Equivalent to `S(initializer_list<T>(bil))` |
   > `S u = bil;` |  | If `bil` is empty, equivalent to `S u;`, otherwise equivalent to `S u = initializer_list<T>(bil);` |
   > `[…]`

### 2214(i). Clarify `basic_ios::init` call restrictions

Section: 31.5.4.2 [basic.ios.cons] Status: Open Submitter: Andrey Semashev Opened: 2012-11-09 Last modified: 2026-06-09

Priority: 4

View all other issues in [basic.ios.cons].

View all issues with Open status.

Discussion:

There is an ambiguity in how `std::basic_ios::init` method (31.5.4.2 [basic.ios.cons]) can be used in the derived class. The Standard only specify the state of the `basic_ios` object after the call completes. However, in `basic_ios` default constructor description (31.5.4.2 [basic.ios.cons]) there is this sentence:

> Effects: Constructs an object of class `basic_ios` (31.5.2.8 [ios.base.cons]) leaving its member objects uninitialized. The object shall be initialized by calling `basic_ios::init` before its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.

This restriction hints that `basic_ios::init` should be called exactly once before the object can be used or destroyed, because `basic_ios::init` may not know whether it was called before or not (i.e. whether its members are actually uninitialized or are initialized by the previous call to `basic_ios::init`). There is no such restriction in the `basic_ios::init` preconditions so it is not clear whether it is allowed to call `basic_ios::init` multiple times or not.

This problem has already affected publicly available implementations. For example, Microsoft Visual C++ STL introduces a memory leak if `basic_ios::init` is called multiple times, while GCC 4.7 and STLPort reinitialize the `basic_ios` object correctly without memory leak or any other undesired effects. There was a discussion of this issue on Boost developers mailing list, and there is a test case that reproduces the problem. The test case is actually a bug report for my Boost.Log library, which attempts to cache `basic_ostream`-derived objects internally to avoid expensive construction and destruction. My stream objects allowed resetting the stream buffer pointers the stream is attached to, without requiring to destroy and construct the stream.

My personal view of the problem and proposed resolution follows.

While apparently the intent of `basic_ios::init` is to provide a way to initialize `basic_ios` after default construction, I see no reason to forbid it from being called multiple times to reinitialize the stream. Furthermore, it is possible to implement a conforming `basic_ios` that does not have this restriction.

The quoted above section of the Standard that describes the effects of the default constructor is misleading. The Standard does not mandate any data members of `basic_ios` or `ios_base` (31.5.2 [ios.base]), which it derives from. This means that the implementation is allowed to use non-POD data members with default constructors that initialize the members with particular default values. For example, in the case of Microsoft Visual C++ STL the leaked memory is an `std::locale` instance that is dynamically allocated during `basic_ios::init`, a raw pointer to which is stored within ios_base. It is possible to store e.g. an `unique_ptr` instead of a raw pointer as a member of `ios_base`, the smart pointer will default initialize the underlying raw pointer on default construction and automatically destroy the allocated object upon being reset or destroyed, which would eliminate the leak and allow `basic_ios::init` to be called multiple times. This leads to conclusion that the default constructor of `basic_ios` cannot leave "its member objects uninitialized" but instead performs default initialization of the member objects, which would mean the same thing in case of POD types.

However, I feel that restricting `ios_base` and `basic_ios` members to non-POD types is not acceptable. Since multiple calls to `basic_ios::init` are not forbidden by the Standard, I propose to correct the `basic_ios` default constructor description so that it is allowed to destroy `basic_ios` object without calling `basic_ios::init`. This would imply that any raw members of `basic_ios` and `ios_base` should be initialized to values suitable for destruction (essentially, this means only initializing raw pointers to NULL). The new wording could look like this:

> Effects: Constructs an object of class `basic_ios` (31.5.2.8 [ios.base.cons]) initializing its member objects to unspecified state, only suitable for `basic_ios` destruction. The object shall be initialized by calling `basic_ios::init` before its first use; otherwise the behavior is undefined.

This would remove the hint that `basic_ios::init` must be called exactly once. Also, this would remove the requirement for `basic_ios::init` to be called at all before the destruction. This is also an important issue because the derived stream constructor may throw an exception before it manages to call `basic_ios::init` (for example, if the streambuf constructor throws), and in this case the `basic_ios` destructor has undefined behavior.

To my mind, the described modification is sufficient to resolve the issue. But to emphasize the possibility to call `basic_ios::init` multiple times, a remark or a footnote for `basic_ios::init` postconditions could be added to explicitly state the semantics of calling it multiple times. The note could read as follows:

> The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.

[2013-04-20, Bristol]

Alisdair: The current wording is unclear but the proposed resolution is wrong

Solution: Clarify that `init` must be called once and only once. Move then to review.

[2021-07-29 Tim comments]

The requirement that "`init` must be called once and only once" conflicts with the disposition of LWG 135(i).

Previous resolution [SUPERSEDED]

This wording is relative to N3485.

1. Edit 31.5.4.2 [basic.ios.cons] as indicated:

   >     basic_ios();

   > > -2- Effects: Constructs an object of class `basic_ios` (31.5.2.8 [ios.base.cons]) leaving its member objects uninitializedinitializing its member objects to unspecified state, only suitable for `basic_ios` destruction. The object shall be initialized by calling `basic_ios::init` before its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.

   >     void init(basic_streambuf<charT,traits>* sb);

   > > Postconditions: The postconditions of this function are indicated in Table 128.

   > > -?- Remarks: The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.

[Brno 2026-06-09; LWG issue processing]

Review history. Would like to accept this. MSVC still requires exactly one call to `init`. Tim to contact implementers to see if changing this would be acceptable.

Jonathan would like to make the default constructor set `badbit` and not leave it so that touching the stream is undefined. That would make it safe to attempt I/O operations (they'd just fail) but e.g. `getloc()` could not succeed for MSVC which doesn't allocate a locale until `init` is called. Changing that would need a paper.

[Brno 2026-06-09; Jonathan provides new wording]

Fix grammar.

Proposed resolution:

This wording is relative to N5046.

1. Edit 31.5.4.2 [basic.ios.cons] as indicated:

   >     basic_ios();

   > > -2- Effects: Constructs an object of class `basic_ios` (31.5.2.8 [ios.base.cons]) leaving its member objects uninitializedinitializing its member objects to unspecified state, only suitable for `basic_ios` destruction. The object shall be initialized by calling `basic_ios::init` before its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.

   >     void init(basic_streambuf<charT,traits>* sb);

   > > Postconditions: The postconditions of this function are indicated in Table 142.

   > > -?- Remarks: The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the state described in Table 142.

### 2215(i). (unordered) associative container functors should be `CopyConstructible`

Section: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-11-14 Last modified: 2015-10-22

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Open status.

Discussion:

The requirements on the functors used to arrange elements in the various associative and unordered containers are given by a set of expressions in tables 102 — Associative container requirements, and 103 — Unordered associative container requirements. In keeping with Library convention these expressions make the minimal requirements necessary on their types. For example, we have the following 3 row extracts for the unordered containers:

Expression | Assertion/note pre-/post-condition
    X(n, hf, eq)
    X a(n, hf, eq)
| Requires: `hasher` and `key_equal` are `CopyConstructible`.
    X(n, hf)
    X a(n, hf)
| Requires: `hasher` is `CopyConstructible` and `key_equal` is `DefaultConstructible`.
    X(n)
    X a(n)
| Requires: `hasher` and `key_equal` are `DefaultConstructible`.

However, the signature for each class template requires that the functors must effectively be `CopyConstructible` for each of these expressions:

>     template <class Key,
>               class T,
>               class Hash  = hash<Key>,
>               class Pred  = std::equal_to<Key>,
>               class Allocator = std::allocator<std::pair<const Key, T> > >
>     class unordered_map
>     {
>       ...

>       // construct/destroy/copy
>       explicit unordered_map(size_type n = see below,
>                              const hasher& hf = hasher(),
>                              const key_equal& eql = key_equal(),
>                              const allocator_type& a = allocator_type());

>       ...
>     }

The letter of the standard can be honored as long as implementors recognize their freedom to split this one signature into multiple overloads, so that the documented default arguments (requiring a `CopyConstructible` functor) are not actually passed as default arguments.

As we look into the requirements for the copy constructor and copy-assignment operator, the requirements are even more vague, as the explicit requirements on the functors are not called out, other than saying that the functors are copied.

Must the functors be `CopyAssignable`? Or is `CopyConstructible` sufficient in this case? Do we require that the functors be `Swappable` so that the copy-swap idiom can be deployed here? Note that a type that is both `CopyConstructible` and `CopyAssignable` is still not guaranteed to be `Swappable` as the user may delete the `swap` function for their type in their own namespace, which would be found via ADL.

Some clean-up of the requirements table looks necessary, to at least document the assignment behavior. In addition, we should have clear guidance on whether these functors should always be `CopyConstructible`, as suggested by the class template definitions, or if the requirement tables are correct and we should explicitly split up the constructors in the (unordered) associative containers to no longer use default (function) arguments to obtain their defaulted functors.

I recommend the simplest solution would be to always require that the functors for (unordered) associative containers be `CopyConstructible`, above the requirements tables themselves, so that the issue need not be addressed within the tables. I suggest that the assignment operators for these containers add the requirement that the functors be `Swappable`, rather than forwarding the corresponding `Assignable` requirement.

[2013-03-15 Issues Teleconference]

Moved to Open.

Alisdair to propose wording.

[2014-06-08, Daniel comments]

The area of this issue partially overlaps what LWG 2227(i) addresses.

[2015-10-20, Daniel comments]

The revised resolution of LWG 2227(i) should resolve this issue as well. It follows the recommendations of the submitter to require `CopyConstructible` requirements for the function objects owned by containers, but it does not impose any further fundamental requirements.

Proposed resolution:

See the resolution of LWG 2227(i).

### 2216(i). `regex_replace(basic_string)` allocator handling

Section: 28.6.10.4 [re.alg.replace] Status: New Submitter: Jeffrey Yasskin Opened: 2012-11-26 Last modified: 2016-01-28

Priority: 3

View all other issues in [re.alg.replace].

View all issues with New status.

Discussion:

>     template <class traits, class charT, class ST, class SA>
>       basic_string<charT, ST, SA>
>       regex_replace(const basic_string<charT, ST, SA>& s,
>           const basic_regex<charT, traits>& e,
>           const charT* fmt,
>           regex_constants::match_flag_type flags =
>     	    regex_constants::match_default);

and friends are documented as

> Constructs an empty string result of type `basic_string<charT, ST, SA>` and calls `regex_replace(back_inserter(result), s.begin(), s.end(), e, fmt, flags)`.

This appears to require the result to have a default-constructed allocator, which isn't even possible for all allocator types. I suspect the allocator should be copied from 's' instead. Possibly there should be an additional defaulted argument to override the allocator of the result.

Proposed resolution:

### 2220(i). Under-specification of `operator==` for `regex_token_iterator`

Section: 28.6.11.2.3 [re.tokiter.comp] Status: Ready Submitter: Pete Becker Opened: 2012-11-21 Last modified: 2026-06-09

Priority: 3

View all issues with Ready status.

Discussion:

Consider the following example:

>     std::string str0("x");
>     std::regex rg0("a");
>     std::regex_token_iterator it0(str0.begin(), str0.end(), rg0, -1); // points at "x" in str0
>     std::string str1("x");
>     std::regex rg1("b");
>     std::regex_token_iterator it1(str1.begin(), str1.end(), rg1, -1); // points at "x" in str1

28.6.11.2.3 [re.tokiter.comp] p1 says that `it0.operator==(it1)` returns true "if `*this` and `right` are both suffix iterators and `suffix == right.suffix`"; both conditions are satisfied in this example. It does not say that they must both be iterators into the same sequence, nor does it say (as general iterator requirements do) that they must both be in the domain of `==` in order for the comparison to be meaningful. It's a simple statement: they're equal if the strings they point at compare equal. Given this being a valid comparison, the obtained result of "true" looks odd.

The problem is that for iterator values prior to the suffix iterator, equality means the same regular expression and the same matched sequence (both uses of "same" refer to identity, not equality); for the suffix iterator, equality means that the matched sequences compare equal.

[2014-02-10]

Priority set to 2

[2018-08-20 Casey adds a proposed resolution]

Priority changed to 3.

Marshall notes that iterator comparisons typically require the iterators to denote elements of the same sequence.
Previous resolution [SUPERSEDED]

This wording is relative to N4762.

- Modify 28.6.11.2.3 [re.tokiter.comp] as follows:

  >     bool operator==(const regex_token_iterator& right) const;

  > -?- Expects: `*this` and `right` are both end-of-sequence iterators or both have the same underlying sequence.

  > -1- Returns: `true` if `*this` and `right` are both end-of-sequence iterators, or if […]

  >     bool operator!=(const regex_token_iterator& right) const;

  > -?- Expects: `*this` and `right` are both end-of-sequence iterators or both have the same underlying sequence.

  > -2- Returns: `!(*this == right)`.

[2018-08-23 Casey revises the P/R in response to LWG feedback]

Previous resolution [SUPERSEDED]

This wording is relative to N4762.

- Modify 28.6.11.2.3 [re.tokiter.comp] as follows:

  >     bool operator==(const regex_token_iterator& right) const;

  > -?- Expects: At least one of `*this` and `right` is an end-of-sequence iterator, or both `*this` and `right` have the same underlying sequence.

  > -1- Returns: `true` if `*this` and `right` are both end-of-sequence iterators, or if […]

  >     bool operator!=(const regex_token_iterator& right) const;

  > -?- Expects: At least one of `*this` and `right` is an end-of-sequence iterator, or both `*this` and `right` have the same underlying sequence.

  > -2- Returns: `!(*this == right)`.

[2024-10-03; Jonathan rebases the wording on the latest WP]

[Brno 2026-06-09; Change status Open → Ready.]

Proposed resolution:

This wording is relative to N4988.

- Modify 28.6.11.2.3 [re.tokiter.comp] as follows:

  >     bool operator==(const regex_token_iterator& right) const;

  > -?- Preconditions: At least one of `*this` and `right` is an end-of-sequence iterator, or `*this` and `right` have the same underlying sequence.

  > -1- Returns: `true` if `*this` and `right` are both end-of-sequence iterators, or if `*this` and `right` are both suffix iterators and `suffix == right.suffix`; otherwise returns `false` if `*this` or `right` is an end-of-sequence iterator or a suffix iterator. Otherwise returns `true` if `position == right.position`, `N == right.N`, and `subs == right.subs`. Otherwise returns `false`.

### 2227(i). Stateful comparison objects in associative containers

Section: 23.2.7 [associative.reqmts] Status: Open Submitter: Juan Soulie Opened: 2012-12-19 Last modified: 2019-04-23

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Open status.

Discussion:

Table 102 in 23.2.7 [associative.reqmts]/8 states on expression `a.key_comp()` that it "returns the comparison object out of which a was constructed". At the same time, 23.2.2 [container.requirements.general]/8 states (starting in the third line) that "...Any `Compare`, `Pred`, or `Hash` objects belonging to `a` and `b` shall be swappable and shall be exchanged by unqualified calls to non-member swap...". This is problematic for any compliant implementation, since once swapped the container cannot return the comparison object out of which it was constructed unless incurring in storing an otherwise needless object.

The simple solution is to correct that statement in Table 102, but I believe this is part of a larger problem of underspecified behavior: The new standard has made an effort in regards to allocators and now fully specifies what happens to stateful allocator objects. It has even specified what happens to stateful `hasher` and `key_equal` members of unordered containers (they propagate), but it says nothing about stateful comparison objects of (ordered) associative containers, except for the statement in 23.2.2 [container.requirements.general]/8 referred above and only related to `swap`.

For example, it is unclear to me what is specified to happen on an assignment: should the comparison object be copied/moved along with the elements, or should the left-hand side object keep its own? Maybe this has been intentionally left unspecified with the purpose of compatibility with C++98, which I understand it specified that comparison objects were kept for the entire life of the container (like allocators) — an unfortunate choice. But anyway, the segment of 23.2.2 [container.requirements.general] quoted above seems to break any possible backwards compatibility with C++98 in this regard.

Therefore, taking into consideration consistency with how this is dealed with for unordered associative containers, I propose that Table 102 is modified as follows:

- The row for expression `a.key_comp()` is changed so that its "assertion/note pre-/post-condition" reads "Returns `a`'s comparison object."

- A new row is added at the appropriate location (which I believe would be after "X(il)" row), with:

  > Table 102 — Associative container requirements (in addition to container)
  > Expression | Return type | Assertion/note pre-/post-condition | Complexity
  > --- | --- | --- | ---
  > `X(b) X a(b)` | `X` | Copy constructor. In addition to the requirements of Table 96, copies the comparison object. | Linear in `b.size()`
  > `a = b` | `X&` | Copy assignment operator. In addition to the requirements of Table 96, copies the comparison object. | Linear in `a.size()` and `b.size()`

[2013-03-15 Issues Teleconference]

Moved to Review.

[2013-04-18, Bristol]

STL: can't believe we don't specify this already. this is totally necessary

Alisdair: how does it do this? copy construction? assignment?

Also need it for move.

STL: we already specify this for constructing from a comparator, not during copy construction though.

Jonathan: don't like wording, should say "`key_compare` is `CopyConstructible`. Uses `b.key_comp()` as a comparison object."

STL: we get it right for unordered!

Jonathan: can't wordsmith this now, but I think implementations do the right thing.

Alisdair: not sure what right thing is for moves. Also we say nothing about propagating allocators to functors.

Moved to Open.

[2015-02 Cologne]

TK: There's no need for fine-grained propagate/not-propagate control. If you don't want to propagate the predicate, you can simply construct or insert from an iterator range.

VV: libstdc++ already implements the resolution of this issue.

GR: There are a couple of other problems. We don't specify move constructor and move assignment for maps. Those are just general.

TK: General container requirements already describe the semantics for {copy,move}-{construction,assignment}, so it doesn't seem that there's room for choice in `std::map` assignments. `unordered_map` is different, though.

[Note: Check what general container requirements say about container equality.]

DK will draft wording. The decision is to unambiguously make all {copy,move}-{construction,assignment} operations endow the LHS with the exact state of the RHS, including all predicates and hash function states.

Conclusion: Update wording, revisit later.

[2015-05-06 Lenexa: Waiting for updated wording]

Previous resolution [SUPERSEDED]:

> This wording is relative to N3485.

> 1. Change Table 102 as indicated:

>    > Table 102 — Associative container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition | Complexity
>    > --- | --- | --- | ---
>    > `…`
>    > `X(il)` | `` | Same as `X(il.begin(), il.end())`. | same as `X(il.begin(), il.end())`.
>    > `X(b) X a(b)` | `` | Copy constructor. In addition to the requirements of Table 96, copies the comparison object. | Linear in `b.size()`
>    > `a = b` | `X&` | Copy assignment operator. In addition to the requirements of Table 96, copies the comparison object. | Linear in `a.size()` and `b.size()`
>    > `…`
>    > `a.key_comp()` | `X::key_compare` | rReturns the `a`'s comparison object out of which a was constructed. | constant

[2015-10-19 Daniel comments and provides alternative wording]

The current standard is especially unclear in regard to what effects move operations of unordered/associative containers should have. We have one example that is standardized exactly in this way by looking at 23.6.4.3 [priqueue.cons.alloc] p7:

>     template <class Alloc> priority_queue(priority_queue&& q, const Alloc& a);

> > -7- Effects: Initializes `c` with `std::move(q.c)` as the first argument and `a` as the second argument, and initializes `comp` with `std::move(q.comp)`

A similarly comparable example are the move-operations of `std::unique_ptr` in regard to the deleter (when this is no a reference), which also respect move-capabilities of that function object.

We have wording from C++98 for associative containers (but not for unordered containers!) that was never adjusted to C++11 move-semantics in 23.2.7 [associative.reqmts] p12:

> When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference. When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.

The second sentence of this wording is problematic for several reasons:

1. It only talks about copy operations, not about move operations, except that the term "assignment" without leading "copy" is a bit ambigious (albeit it seems clear in the complete context).

2. It is not really clear how to interpret "as if that comparison object had been passed to the target container in its constructor" for an assignment operation. A possible but not conclusive interpretation could be that this is wording supporting a "copy-via-swap" idiom.

3. There does not exist similar wording for unordered containers, except that Table 102 provides entries for copy construction and copy assignment of the containers whose wording just talks of "copies" in either case.

Existing implementations differ already:

1. Visual Studio 2015 uses copy construction and copy assignment for the two copy operations but uses swap operations for the move operations.

2. GCC's libstdc++ performs copy construction and copy assignment for the two copy operations and for the two move operations, respectively

3. clang++'s libc++ performs copy/move construction and copy/move assignment for the corresponding four copy/move operations

The alternative wording provided below attempts to clarify that container copy/move operations perform the corresponding copy/move operations on the owned function objects.

In addition the wording also resolves LWG 2215(i): I believe that the current wording should require that container function objects should meet the `CopyConstructible` requirements. Adding this general requirement also fixes the underspecified requirements of the accessor functions `key_comp()` and `value_comp()`.

I don't think that a general requirement for `Swappable` is needed, only the member `swap` function currently requires this. Nonetheless the wording below does support stateful functors that are also moveable or move-assignable, therefore the specified semantics in terms of move operations.

I should add the following warning, though: If this proposed wording would be accepted, there is a little chance of code breakage, because the current wording can be read that in general there is no requirement that the container functors are `CopyConstructible`. The following code example is accepted by gcc + libstd++:

>     #include <map>
>     #include <utility>
>     #include <iostream>

>     struct Cmp {
>       Cmp() = default;
>       Cmp(const Cmp&) = delete;
>       Cmp(Cmp&&) = delete;
>       Cmp& operator=(const Cmp&) = delete;
>       Cmp& operator=(Cmp&&) = delete;
>       template<class T>
>       bool operator()(const T& x, const T& y) const
>       {
>         return x < y;
>       }
>     };

>     typedef std::map<int, int, Cmp> MyMap;

>     int main() {
>       MyMap m;
>       std::cout << (m.find(12) == m.end()) << std::endl;
>     }

Previous resolution [SUPERSEDED]:

> This wording is relative to N4527.

> 1. Change 23.2.7 [associative.reqmts] p8 as indicated:

>    > -8- In Table 101, `X` denotes an associative container class, `a` denotes a value of type `X`, `b` denotes a possibly `const` value of type `X`, `rv` denotes a non-`const` rvalue of type `X`, `u` denotes the name of a variable being declared, […]

> 2. Change Table 101 as indicated:

>    > Table 101 — Associative container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition | Complexity
>    > --- | --- | --- | ---
>    > `…`
>    > `X::key_compare` | `Compare` | Requires: `Compare` is `CopyConstructible`. defaults to `less<key_type>` | compile time
>    > `X(c) X u(c);` | `` | Requires: `key_compare` is `CopyConstructible`. Effects: Constructs an empty container. Uses a copy of `c` as a comparison object. | […]
>    > `…`
>    > `X(i,j,c) X u(i,j,c);` | `` | Requires: `key_compare` is `CopyConstructible`. `value_type` is `EmplaceConstructible` into `X` from `*i`. Effects: Constructs an empty container and inserts elements from the range `[i, j)` into it; uses `c` as a comparison object. | […]
>    > `…`
>    > `X(il)` | `` | Same as `X(il.begin(), il.end())`. | same as `X(il.begin(), il.end())`.
>    > `X(b) X a(b)` | `` | (In addition to the requirements of Table 95) Effects: Copy constructs the comparison object of `a` from the comparison object of `b`. | Linear in `b.size()`
>    > `X(rv) X a(rv)` | `` | (In addition to the requirements of Table 95 and Table 98) Effects: Move constructs the comparison object of `a` from the comparison object of `rv`. | constant
>    > `a = b` | `X&` | (In addition to the requirements of Table 95 and Table 98) Requires: `key_compare` is `CopyAssignable`. Effects: Copy assigns the comparison object of `b` to the comparison object of `a`. | Linear in `a.size()` and `b.size()`
>    > `a = rv` | `X&` | (In addition to the requirements of Table 95 and Table 98) Requires: `key_compare` is `MoveAssignable`. Effects: Move assigns from the comparison object of `rv` to the comparison object of `a`. | Linear
>    > `…`
>    > `a.key_comp()` | `X::key_compare` | rReturns the `a`'s comparison object out of which a was constructed. | constant

> 3. Change 23.2.7 [associative.reqmts] p12 as indicated:

>    > -12- When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference. When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.

> 4. Change 23.2.8 [unord.req] p11 as indicated:

>    > -11- In Table 102: `X` denotes an unordered associative container class, `a` denotes a value of type `X`, `b` denotes a possibly `const` value of type `X`, `rv` denotes a non-`const` rvalue of type `X`, […]

> 5. Change Table 102 as indicated:

>    > Table 102 — Unordered associative container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition | Complexity
>    > --- | --- | --- | ---
>    > `…`
>    > `X::hasher` | `Hash` | Requires: `Hash` is `CopyConstructible`. `Hash` shall be a unary function object type such that the expression `hf(k)` has type `std::size_t`. | compile time
>    > `X::key_equal` | `Pred` | Requires: `Pred` is `CopyConstructible`. `Pred` shall be a binary predicate that takes two arguments of type `Key`. `Pred` is an equivalence relation. | compile time
>    > `…`
>    > `X(n, hf, eq) X a(n, hf, eq)` | `X` | Requires: `hasher` and `key_equal` are `CopyConstructible`. Effects: […] | […]
>    > `X(n, hf) X a(n, hf)` | `X` | Requires: `hasher` is `CopyConstructible` and `key_equal` is `DefaultConstructible`. Effects: […] | […]
>    > `…`
>    > `X(i, j, n, hf, eq) X a(i, j, n, hf, eq)` | `X` | Requires: `hasher` and `key_equal` are `CopyConstructible`. `value_type` is `EmplaceConstructible` into `X` from `*i`. Effects: […] | […]
>    > `X(i, j, n, hf) X a(i, j, n, hf)` | `X` | Requires: `hasher` is `CopyConstructible` and `key_equal` is `DefaultConstructible`. `value_type` is `EmplaceConstructible` into `X` from `*i`. Effects: […] | […]
>    > `…`
>    > `X(b) X a(b)` | `X` | Copy constructor. In addition to the requirements of Table 95, copies the hash function, predicate, and maximum load factor. (In addition to the requirements of Table 95) Effects: Copy constructs the hash function, predicate, and maximum load factor of `a` from the corresponding objects of `b`. | Average case linear in `b.size()`, worst case quadratic.
>    > `X(rv) X a(rv)` | `X` | (In addition to the requirements of Table 95 and Table 98) Effects: Move constructs the hash function, predicate, and maximum load factor of `a` from the corresponding objects of `rv`. | constant
>    > `a = b` | `X&` | Copy assignment operator. In addition to the requirements of Table 95, copies the hash function, predicate, and maximum load factor. (In addition to the requirements of Table 95 and Table 98) Requires: `hasher` and `key_equal` are `CopyAssignable`. Effects: Copy assigns the hash function, predicate, and maximum load factor of `b` to the corresponding objects of `a`. | Average case linear in `b.size()`, worst case quadratic.
>    > `a = rv` | `X&` | (In addition to the requirements of Table 95 and Table 98) Requires: `hasher` and `key_equal` are `MoveAssignable`. Effects: Move assigns the hash function, predicate, and maximum load factor from `rv` to the corresponding objects of `a`. | Linear
>    > `…`

[2016-08-07]

Daniel removes the previously proposed wording to work on revised wording.

[2019-04-22, Billy comments]

In addition to the Cpp17CopyConstructible discussion going on there, I think we need to require that calling the comparison function when `Compare` itself is `const` needs to produce the same answer as if `Compare` is non-`const`.

Proposed resolution:

### 2237(i). `<cuchar>` macros

Section: 27.5 [c.strings] Status: Core Submitter: Jason Merrill Opened: 2013-01-29 Last modified: 2026-06-10

Priority: 4

View other active issues in [c.strings].

View all other issues in [c.strings].

View all issues with Core status.

Discussion:

Apparently C1X changes `__STDC_UTF_16__` and `__STDC_UTF_32__` from macros defined in `uchar.h` (and reflected in C++ by Table 79) to be predefined by the compiler. Do we want to do the same?

[Brno 2026-06-09; Change status New → Core]

In C++14 these macros were defined in a table showing them as macros in `<cuchar>`, and referring to the C Unicode TR, ISO/IEC TR 19769:2004, where the macros were defined in the header. But in the final C11 standard, the macros were predefined by the compiler not in the header. So when C++17 rebased on C11 and referred to the `uchar.h` from the C standard, we lost any reference to those macros.

An editorial change for C++17 removed the table mentioning those macros, following an arguably incorrect editorial change to "remove an outdated reference to the C Unicode TR". Although `uchar.h` is in C11, we lost any mention of the macros by saying that our `uchar.h` is the same as C `uchar.h`. So those macros now have no existence at all in C++. They're not inherited from the C version of `uchar.h` and they're not predefined by a C++ compiler.

If we want to copy those macros from C, they should be in 15.12 [cpp.predefined], so Core should decide on that.

Proposed resolution:

### 2238(i). Problematic iterator-pair constructor of containers

Section: 27.5 [c.strings] Status: Open Submitter: Johannes Schaub Opened: 2013-02-02 Last modified: 2016-08-09

Priority: 3

View other active issues in [c.strings].

View all other issues in [c.strings].

View all issues with Open status.

Discussion:

The non-explicit nature of the iterator-pair constructor of containers, such a

>     template <class InputIterator>
>     vector(InputIterator first, InputIterator last, const Allocator& = Allocator());

can be selected in unexpected situations, leading to a hard runtime error, as demonstrated by the following example:

>     #include <vector>

>     void f(std::vector<char> v){ /* ... */}

>     int main() {
>       f({"A", "B"});
>     }

The actually intended initializer-list constructor isn't feasible here, so the best match is the constructor template

>     template <class InputIterator>
>     vector(InputIterator first, InputIterator last, const Allocator& = Allocator());

This compiles, but will result in code running amok. The potential trap (that cannot be easily detected by the library implementation) could be reduced by making this constructor explicit. It would still have the effect to be selected here, but the code would be ill-formed, so the programmer gets a clear message here.

[2014-06 Rapperswil]

JW: can't fix this, don't want to touch this, Do The Right Thing clause has been a source of tricky issues. only really happens with string literals, that's the only way to create an array that isn't obviously an array

GR: want to see paper

AM: is it only string literals, or also UDLs?

STL: maybe, but we don't need to deal with that. This is only a problem in a very specific case

Leave as Open.

Proposed resolution:

### 2262(i). Requirement for `unique_ptr<T>::get_deleter()(p)` to be able to destroy the `unique_ptr`

Section: 20.3.1.3 [unique.ptr.single] Status: Open Submitter: Rob Desbois Opened: 2013-05-15 Last modified: 2017-03-21

Priority: 3

View other active issues in [unique.ptr.single].

View all other issues in [unique.ptr.single].

View all issues with Open status.

Discussion:

N3337 20.3.1.3.6 [unique.ptr.single.modifiers] contains 2 non-normative notes stating:

> [para 4]: "The order of these operations is significant because the call to `get_deleter()` may destroy `*this`."

> [para 5]: "The postcondition does not hold if the call to `get_deleter()` destroys `*this` since `this->get()` is no longer a valid expression."

It seems this wording was created to resolve 998(i) due to the possibility that a `unique_ptr` may be destroyed through deletion of its stored pointer where that directly or indirectly refers to the same `unique_ptr`. If `unique_ptr` is required to support circular references then it seems this must be normative text: an implementation is currently allowed to operate on `*this` after the assignment and deletion specified in para 4, since this is only 'disallowed' by the non-normative note.

I propose the following draft rewording:

[para 4]: Effects: assigns `p` to the stored pointer, and then if the old value of the stored pointer, `old_p`, was not equal to `nullptr`, calls `get_deleter()(old_p)`. No operation shall be performed after the call to `get_deleter()(old_p)` that requires `*this` to be valid, because the deletion may destroy `*this` if it is referred to directly or indirectly by the stored pointer. [Note: The order of these operations is significant because the call to `get_deleter()` may destroy `*this`. — end note]

[para 5]: Postconditions: If the call `get_deleter()(old_p)` destroyed `*this`, none. Otherwise, `get() == p`. [Note: The postcondition does not hold if the call to `get_deleter()` destroys `*this` since `this->get()` is no longer a valid expression. — end note]

I expect it will also be necessary to amend the requirements for a deleter, so in addition:

20.3.1.3 [unique.ptr.single] [para 1]: The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value `d` of type `D` and a value `ptr` of type `unique_ptr<T, D>::pointer`, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter. Where `D` is not an lvalue reference type, `d(ptr)` shall be valid if `ptr` refers directly or indirectly to the invoking `unique_ptr` object.

[2013-10-05, Stephan T. Lavavej comments and provides alternative wording]

In Chicago, we determined that the original proposed change to 20.3.1.3 [unique.ptr.single]/1 was insufficient, because `d` might be a reference to a deleter functor that's destroyed during self-destruction.

We believed that 20.3.1.3.6 [unique.ptr.single.modifiers]/4 was already sufficiently clear. The Standard occasionally prevents implementations of `X` from doing various things, through the principle of "nothing allows `X` to fail in that situation". For example, `v.push_back(v[0])` is required to work for non-empty vectors because nothing allows that to fail. In this case, the intent to allow self-destruction is already clear.

Additionally, we did not believe that 20.3.1.3.6 [unique.ptr.single.modifiers]/5 had to be changed. The current note is slightly squirrely but it does not lead to confusion for implementers or users.

Previous resolution from Rob Desbois:

> 1. Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:

>    > The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value `d` of type `D` and a value `ptr` of type `unique_ptr<T, D>::pointer`, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter. Where `D` is not an lvalue reference type, `d(ptr)` shall be valid if `ptr` refers directly or indirectly to the invoking `unique_ptr` object.

> 2. Edit 20.3.1.3.6 [unique.ptr.single.modifiers] p4+5 as indicated:

>    >     void reset(pointer p = pointer()) noexcept;

>    > > -3- Requires: The expression `get_deleter()(get())` shall be well formed, shall have well-defined behavior, and shall not throw exceptions.

>    > > -4- Effects: assigns `p` to the stored pointer, and then if the old value of the stored pointer, `old_p`, was not equal to `nullptr`, calls `get_deleter()(old_p)`. No operation shall be performed after the call to `get_deleter()(old_p)` that requires `*this` to be valid, because the deletion may destroy `*this` if it is referred to directly or indirectly by the stored pointer. [Note: The order of these operations is significant because the call to `get_deleter()` may destroy `*this`. — end note]

>    > > -5- Postconditions: If the call `get_deleter()(old_p)` destroyed `*this`, none. Otherwise, `get() == p`. [Note: The postcondition does not hold if the call to `get_deleter()` destroys `*this` since `this->get()` is no longer a valid expression. — end note]

Previous resolution [SUPERSEDED]:

> This wording is relative to N3691.

> 1. Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:

>    > The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value `d` of type `D` and a value `ptr` of type `unique_ptr<T, D>::pointer`, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter. `d(ptr)` shall be valid even if it triggers the destruction of `d` or (if `D` is an lvalue reference to function object type) the function object that `d` refers to.

[2015-05, Lenexa]

After some discussion in Lenexa there was some wavering on if the added sentence is necessary. Here is example code that demonstrates why the extra sentence is necessary. In this example the call to `d(ptr)` is valid, however the deleter references `*this` after destructing its element:

>     #include <cassert>
>     #include <memory>
>     #include <iostream>

>     class Deleter
>     {
>         int state_ = 0;

>         enum
>         {
>             destructed            = -4,
>             self_move_assigned    = -3,
>             move_assigned_from    = -2,
>             move_constructed_from = -1
>         };
>     public:
>         ~Deleter() {state_ = destructed;}

>         Deleter() = default;
>         Deleter(Deleter const&) = default;
>         Deleter& operator=(Deleter const&) = default;

>         Deleter(Deleter&& a) noexcept
>             : state_(a.state_)
>         {a.state_ = move_constructed_from;}

>         Deleter& operator=(Deleter&& a) noexcept
>         {
>             if (this == &a)
>                 state_ = self_move_assigned;
>             else
>             {
>                 state_ = a.state_;
>                 a.state_ = move_assigned_from;
>             }
>             return *this;
>         }

>         Deleter(int state)
>             : state_(state)
>         {
>             assert(state >= 0);
>         }

>         template <class T>
>         void
>         operator()(T* t) const
>         {
>             std::cout << "Deleter beginning operator()(T*)\n";
>             std::cout << "The deleter = " << *this << '\n';
>             std::cout << "Deleter about to destruct the X.\n";
>             delete t;
>             std::cout << "Deleter has destructed the X.\n";
>             std::cout << "The deleter = " << *this << '\n';
>             std::cout << "Deleter ending operator()(T*)\n";
>         }

>         friend
>         std::ostream&
>         operator<<(std::ostream& os, const Deleter& a)
>         {
>             switch (a.state_)
>             {
>             case destructed:
>                 os << "**destructed**";
>                 break;
>             case self_move_assigned:
>                 os << "self_move_assigned";
>                 break;
>             case move_assigned_from:
>                 os << "move_assigned_from";
>                 break;
>             case move_constructed_from:
>                 os << "move_constructed_from";
>                 break;
>             default:
>                 os << a.state_;
>                 break;
>             }
>             return os;
>         }
>     };

>     struct X
>     {
>         Deleter deleter_{1};
>     };

>     int main()
>     {
>         auto xp = new X;
>         {
>             std::unique_ptr<X, Deleter&> p(xp, xp->deleter_);
>             std::cout << "unique_ptr is constructed.\n";
>             std::cout << "The deleter = " << p.get_deleter() << '\n';
>             std::cout << "Destructing unique_ptr...\n";
>         }
>         std::cout << "unique_ptr is destructed.\n";
>     }

Which outputs:

>     unique_ptr is constructed.
>     The deleter = 1
>     Destructing unique_ptr...
>     Deleter beginning operator()(T*)
>     The deleter = 1
>     Deleter about to destruct the X.
>     Deleter has destructed the X.
>     The deleter = **destructed**
>     Deleter ending operator()(T*)
>     unique_ptr is destructed.

The line "`The deleter = **destructed**`" represents the deleter referencing itself after it has been destructed by the `d(ptr)` expression, but prior to that call returning.

Suggested alternative to the current proposed wording:

> The expression `d(ptr)` shall not refer to the object `d` after it executes `ptr->~T()`.

[2015-07, Telecon]

Geoffrey: Deleter may or may not execute ~T().
Alisdair: After the destructor after the element has run. Say it in words instead of code.
Howard will provide updated wording. Perhaps need both normative and non-normative wording.

[2015-08-03, Howard updates P/R per telecon discussion.]

[2017-03-04, Kona]

This is related to 2751(i), which has been suggested NAD.

STL wants "Effects equivalent to" here - say it in code. Marshall to research.

Proposed resolution:

This wording is relative to N4431.

1. Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:

   > The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type (20.9), lvalue-reference to function, or lvalue-reference to function object type for which, given a value `d` of type `D` and a value `ptr` of type `unique_ptr<T, D>::pointer`, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter. The expression `d(ptr)`, if it destructs the object referred to by `ptr`, shall not refer to the object `d` after it destructs `*ptr`. [Note: The object being destructed may control the lifetime of `d`. — end note]

### 2265(i). 29.3p9 appears to rule out some acceptable executions

Section: 32.5.4 [atomics.order] Status: Open Submitter: Brian Demsky Opened: 2013-06-17 Last modified: 2016-01-28

Priority: 4

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with Open status.

Discussion:

I believe that the following variation on IRIW should admit executions in which `c1 = d1 = 5` and `c2 = d2 = 0`. If this is allowed, then what is sequence of program evaluations for 32.5.4 [atomics.order] p9 that justifies the store to `z`? It seems that 32.5.4 [atomics.order] p9 should not allow this execution because one of the stores to `x` or `y` has to appear earlier in the sequence, each of the `fetch_adds` reads the previous load in the thread (and thus must appear later in the sequence), and 32.5.4 [atomics.order] p9 states that each load must read from the last prior assignment in the sequence.

>     atomic_int x;
>     atomic_int y;
>     atomic_int z;
>     int c1, c2, d1, d2;

>     static void a(void* obj)
>     {
>       atomic_store_explicit(&x, 5, memory_order_relaxed);
>     }

>     static void b(void* obj)
>     {
>       atomic_store_explicit(&y, 5, memory_order_relaxed);
>     }

>     static void c(void* obj)
>     {
>       c1 = atomic_load_explicit(&x, memory_order_relaxed);
>       // this could also be an atomic load if the address depends on c1:
>       c2 = atomic_fetch_add_explicit(&y, c1, memory_order_relaxed);
>     }

>     static void d(void* obj)
>     {
>       d1 = atomic_load_explicit(&y, memory_order_relaxed);
>       d2 = atomic_fetch_add_explicit(&x, d1, memory_order_relaxed);
>     }

>     int user_main(int argc, char** argv)
>     {
>       thrd_t t1, t2, t3, t4;

>       atomic_init(&x, 0);
>       atomic_init(&y, 0);

>       printf("Main thread: creating 4 threads\n");
>       thrd_create(&t1, (thrd_start_t)&a, NULL);
>       thrd_create(&t2, (thrd_start_t)&b, NULL);
>       thrd_create(&t3, (thrd_start_t)&c, NULL);
>       thrd_create(&t4, (thrd_start_t)&d, NULL);

>       thrd_join(t1);
>       thrd_join(t2);
>       thrd_join(t3);
>       thrd_join(t4);
>       printf("c1=%d c2=%d\n",c1,c2);
>       printf("d1=%d d2=%d\n",d1,d2);

>       // Can this store write 1000 (i.e., c1=d1=5, c2=d2=0)?
>       atomic_store(&z, (c1+d1)*100+c2+d2);

>       printf("Main thread is finished\n");

>       return 0;
>     }

It seems that the easiest fix is to allow a load in 32.5.4 [atomics.order] p9 to read from any prior store in the evaluation order.

That said, I would personally advocate the following: It seems to me that C/C++ atomics are in a bit of different situation than Java because:

1. People are expected to use relaxed C++ atomics in potentially racy situations, so it isn't clear that semantics as complicated as the JMM's causality would be sane.

2. People who use C/C++ atomics are likely to be experts and use them in a very controlled fashion. I would be really surprised if compilers would find any real wins by optimizing the use of atomics.

Why not do something like:

There is satisfaction DAG of all program evaluations. Each evaluation observes the values of variables as computed by some prior assignment in the DAG.

There is an edge `x->y` between two evaluations `x` and `y` if:

1. the evaluation `y` observes a value computed by the evaluation `x` or

2. the evaluation `y` is an atomic store, the evaluation `x` is an atomic load, and there is a condition branch c that may depend (intrathread dependence) on `x` and `x-sb->c` and `c-sb->y`.

This seems to allow reordering of relaxed atomics that processors do without extra fence instructions, allows most reorderings by the compiler, and gets rid of satisfaction cycles.

[2015-02 Cologne]

Handed over to SG1.

[2015-05 Lenexa, SG1 response]

This was partially addressed (weasel-worded) in C++14 (See N3786). The remainder is an open research problem. N3710 outlines a "solution" that doesn't have a consensus behind it because it costs performance. We have no better solution at the moment.

Proposed resolution:

### 2267(i). `partial_sort_copy` underspecified for ranges of two different types

Section: 26.8.2.4 [partial.sort.copy] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-01-28

Priority: 3

View all issues with New status.

Discussion:

The signature of this function is:

>     template<class InputIterator, class RandomAccessIterator>
>     RandomAccessIterator
>     partial_sort_copy(InputIterator first, InputIterator last,
>                       RandomAccessIterator result_first,
>                       RandomAccessIterator result_last);

(and the usual overload for an explicitly provided comparison function). The standard says nothing about requirements in the case where the input type (`iterator_traits<InputIterator>::value_type`) and the output type (`iterator_traits<RandomAccessIterator>::value_type`) are different.

Presumably the input type must be convertible to the output type. What's less clear is what the requirements are on the comparison operator. Does the algorithm only perform comparisons on two values of the output type, or does it also perform comparisons on values of the input type, or might it even perform heterogeneous comparisons?

Proposed resolution:

### 2269(i). Container iterators and argument-dependent lookup

Section: 23.2.2 [container.requirements.general] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-01-28

Priority: 4

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with New status.

Discussion:

Consider the following code snippet:

>     #include <vector>
>     #include <algorithm>

>     int main() {
>       std::vector<int> v1(100, 3);
>       std::vector<int> v2(100);
>       copy(v1.begin(), v1.end(), v2.begin());
>     }

It compiles without error on my desktop. Is it required to? I can't find evidence from the standard that it is. In my test `std::copy` was found by argument-dependent lookup because the implementation I used made `std::vector<int>::iterator` a user-defined type defined in namespace `std`. But the standard only requires `std::vector<int>::iterator` to be an implementation specified random access iterator type. I can't find anything requiring it to be a user-defined type at all (and in fact there are reasonable implementation where it isn't), let alone a user defined type defined in a specific namespace.

Since the defining namespace of container iterators is visible to users, should the standard say anything about what that namespace is?

Proposed resolution:

### 2286(i). `stringbuf::underflow()` underspecified

Section: 31.8.2.5 [stringbuf.virtuals] Status: Open Submitter: Sergey Zubkov Opened: 2013-08-29 Last modified: 2018-06-12

Priority: 4

View other active issues in [stringbuf.virtuals].

View all other issues in [stringbuf.virtuals].

View all issues with Open status.

Discussion:

In 31.8.2.5 [stringbuf.virtuals]/1, `basic_stringbuf::underflow()` is specified to unconditionally return `traits::eof()` when a read position is not available.

The semantics of `basic_stringbuf` require, and existing libraries implement it so that this function makes a read position available if possible to do so, e.g. if some characters were inserted into the stream since the last call to `overflow()`, resulting in `pptr() > egptr()`. Compare to the conceptually similar 99 [depr.strstreambuf.virtuals]/15.

[2018-06-06, Billy argues for NAD]

The existing "Any character in the underlying buffer which has been initialized is considered to be part of the input sequence." sentence already describes what the `stringbuf` is supposed to do to the get area. The specific mechanism that the `stringbuf` uses to alter the get area is unspecified because the mechanism by which the `stringbuf` remembers the "high water mark" is unspecified.

Consider the following:

>     stringstream s;
>     s << "Hello";
>     s.seekp(0);
>     string x;
>     s >> x;

Before this P/R, this will store `Hello` in `x`, because the characters `Hello` are initialized. After this P/R, the "written put area" is empty, so it will store the empty string in `x`.

Saying that the initialized part of the string is used already describes what needs to happen here.

[2018-06 Rapperswil Wednesday issues processing]

Billy to provide rationale for closing as NAD.

Proposed resolution:

This wording is relative to N3691.

1. Change 31.8.2.5 [stringbuf.virtuals] as indicated:

   >     int_type underflow();

   > > -1- Returns: If the input sequence has a read position available or the function makes a read position available (as described below), returns `traits::to_int_type(*gptr())`. Otherwise, returns `traits::eof()`. Any character in the underlying buffer which has been initialized is considered to be part of the input sequence.

   > > -?- The function can make a read position available only if `(mode & ios_base::in) != 0` and if the write next pointer `pptr()` is not null and is greater than the current read end pointer `egptr()`. To make a read position available, the function alters the read end pointer `egptr()` to equal `pptr()`.

### 2289(i). `constexpr` guarantees of defaulted functions still insufficient

Section: 22.3.2 [pairs.pair], 22.4.4.2 [tuple.cnstr], 30.5 [time.duration] Status: Open Submitter: Daniel Krügler Opened: 2013-09-09 Last modified: 2026-06-11

Priority: 3

View other active issues in [pairs.pair].

View all other issues in [pairs.pair].

View all issues with Open status.

Discussion:

During the acceptance of N3471 and some similar `constexpr` papers, specific wording was added to `pair`, `tuple`, and other templates that were intended to impose implementation constraints that ensure that the observable `constexpr` "character" of a defaulted function template is solely determined by the required expressions of the user-provided types when instantiated, for example:

> The defaulted move and copy constructor, respectively, of pair shall be a `constexpr` function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a `constexpr` function.

This wording doesn't require enough, especially since the core language via CWG 1358 does now support `constexpr` function template instantiations, even if such function cannot appear in a constant expression (as specified in 7.7 [expr.const]) or as a constant initializer of that object (as specified in [basic.start.init]). The wording should be improved and should require valid uses in constant expressions and as constant initializers instead.

[Lenexa 2015-05-05]

STL : notice order of move/copy and copy/move with "respectively".

General word-smithing; ask for updated wording

Are we happy with this with changes we are suggesting?

unanimous

[2016-12-14, Daniel comments]

LWG 2833(i) overlaps considerably and both should be resolved together.

Previous resolution [SUPERSEDED]

This wording is relative to N3691.

1. Change 22.3.2 [pairs.pair] p2 as indicated:

   > -2- The defaulted move and copy constructor, respectively, of pair shall be a `constexpr` function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a `constexpr` functionAn invocation of the move or copy constructor of `pair` shall be a constant expression (7.7 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor of `pair` shall be a constant initializer for that `pair` object ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects.

2. Change 22.4.4.2 [tuple.cnstr] p2 as indicated:

   > -2- The defaulted move and copy constructor, respectively, of `tuple` shall be a `constexpr` function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a `constexpr` function. The defaulted move and copy constructor of `tuple<>` shall be `constexpr` functionsAn invocation of the move or copy constructor of `tuple` shall be a constant expression (7.7 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor of `tuple` shall be a constant initializer for that `tuple` object ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects. An invocation of the move or copy constructor of `tuple<>` shall be a constant expression, or a constant initializer for that `tuple<>` object, respectively, if the function argument would be constant expression.

3. Change 30.5 [time.duration] p7 as indicated:

   > -7- Remarks: The defaulted copy constructor of duration shall be a `constexpr` function if and only if the required initialization of the member `rep_` for copy and move, respectively, would satisfy the requirements for a `constexpr` function.An invocation of the copy constructor of `duration` shall be a constant expression (7.7 [expr.const]) if the required initialization of the member `rep_` would be a constant expression. An invocation of the copy constructor of `duration` shall be a constant initializer for that `duration` object ( [basic.start.init]) if the required initialization of the member `rep_` would be constant initializers for this subobject.

[2020-06-08 Nina Dinka Ranns comments]

The revised wording provided by LWG 2833(i) should resolve this issue as well.

Proposed resolution:

### 2290(i). Top-level "SFINAE"-based constraints should get a separate definition in Clause 17

Section: 21 [meta] Status: Open Submitter: Daniel Krügler Opened: 2013-09-02 Last modified: 2016-01-28

Priority: 3

View other active issues in [meta].

View all other issues in [meta].

View all issues with Open status.

Discussion:

The current library specification uses at several places wording that is intended to refer to core language template deduction failure at the top-level of expressions (aka "SFINAE"), for example:

> The expression `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to `T` and `U`. Only the validity of the immediate context of the assignment expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

Similar wording can be found in the specification of `result_of`, `is_constructible`, and `is_convertible`, being added to resolve an NB comment by LWG 1390(i) and 1391(i) through N3142.

This wording is necessary to limit speculative compilations needed to implement these traits, but it is also lengthy and repetitive.

[2014-05-19, Daniel suggests a descriptive term]

constrictedly well-formed expression:

An expression e depending on a set of types `A1`, ..., `An` which is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to `A1`, ..., `An`. Only the validity of the immediate context of e is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

[2014-05-20, Richard and Jonathan suggest better terms]

Richard suggested "locally well-formed"

Jonathan suggested "contextually well-formed" and then "The expression ... is valid in a contrived argument deduction context"

[2014-06-07, Daniel comments and improves wording]

The 2014-05-19 suggestion did only apply to expressions, but there are two important examples that are not expressions, but instead are involving an object definition (`std::is_constructible`) and a function definition (`std::is_convertible`), respectively, instead. Therefore I suggest to rephrase the usage of "expression" into "program construct" in the definition of Jonathan's suggestion of "valid in a contrived argument deduction context".

I would like to point out that given the new definition of "valid in a contrived argument deduction context", there are several other places of the Library specification that could take advantage of this wording to improve the existing specification, such as 22.10.17.3 [func.wrap.func] p2, most functions in 20.2.9.3 [allocator.traits.members], and the `**Insertable`, `EmplaceConstructible`, and `Erasable` definitions in 23.2.2 [container.requirements.general], but given that these are not fully described in terms of the aforementioned wording yet, I would recommend to fix them by a separate issue once the committee has agreed on following the suggestion presented by this issue.

[2015-05-05 Lenexa: Move to Open]

...

MC: I think we like the direction but it isn't quite right: it needs some work

JW: I'm prepared to volunteer to move that further, hopefully with the help of Daniel

Roger Orr: should this be Core wording because it doesn't really have anything to do with libraries - the term could then just be used here

AM: Core has nothing to deal with that, though

HT: it seems there is nothing to imply that allows dropping out with an error - maybe that's a separate issue

MC: I'm not getting what you are getting at: could you write an issue? - any objection to move to Open?

...

Proposed resolution:

This wording is relative to N3936.

1. Add the following new definition to [definitions] as indicated:

   valid in a contrived argument deduction context [defns.valid.contr.context]

   A program construct c depending on a set of types `A1`, ..., `An`, and treated as an unevaluated operand (Clause 5) when c is an expression, which is well-formed. Access checking is performed as if in a context unrelated to `A1`, ..., `An`. Only the validity of the immediate context (13.10.3 [temp.deduct]) of c is considered. [Note: The compilation of c can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note].

2. Change Table 49 ("Type property predicates") as indicated:

   > Table 49 — Type property predicates
   > Template | Condition | Preconditions
   > --- | --- | ---
   > `…`
   > `template <class T, class U> struct is_assignable;` | The expression `declval<T>() = declval<U>()` is valid in a contrived argument deduction context ([defns.valid.contr.context]) for types `T` and `U`. well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to `T` and `U`. Only the validity of the immediate context of the assignment expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note] | […]
   > `…`

3. Change 21.3.5.4 [meta.unary.prop] p7 as indicated:

   > -7- Given the following function prototype:

   > >     template <class T>
   > >       add_rvalue_reference_t<T> create() noexcept;

   > the predicate condition for a template specialization `is_constructible<T, Args...>` shall be satisfied if and only if the following variable definition would be well-formed for some invented variable `t` would be valid in a contrived argument deduction context ([defns.valid.contr.context]) for types `T` and `Args...`:

   > >     T t(create<Args>()...);

   > [Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to `T` and any of the `Args`. Only the validity of the immediate context of the variable initialization is considered. [Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

4. Change Table 57 ("Other transformations") as indicated:

   > Table 57 — Other transformations
   > Template | Condition | Comments
   > --- | --- | ---
   > `…`
   > `template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;` | […] | If the expression `INVOKE(declval<Fn>(), declval<ArgTypes>()...)` is valid in a contrived argument deduction context ([defns.valid.contr.context]) for types `Fn` and `ArgTypes...` well formed when treated as an unevaluated operand (Clause 5), the member typedef type shall name the type `decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))`; otherwise, there shall be no member type. Access checking is performed as if in a context unrelated to `Fn` and `ArgTypes`. Only the validity of the immediate context of the expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
   > `…`

5. Change 21.3.7 [meta.rel] p4 as indicated:

   > -4- Given the following function prototype:

   > >     template <class T>
   > >       add_rvalue_reference_t<T> create() noexcept;

   > the predicate condition for a template specialization `is_convertible<From, To>` shall be satisfied if and only if the return expression in the following code would be well-formedvalid in a contrived argument deduction context ([defns.valid.contr.context]) for types `To` and `From`, including any implicit conversions to the return type of the function:

   > >     To test() {
   > >       return create<From>();
   > >     }

   > [Note: This requirement gives well defined results for reference types, `void` types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to `To` and `From`. Only the validity of the immediate context of the expression of the return-statement (including conversions to the return type) is considered. [Note: The evaluation of the conversion can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

### 2303(i). Explicit instantiation of `std::vector<UserType>` broken?

Section: 17.6.3.4 [new.delete.placement] Status: New Submitter: Daniel Krügler Opened: 2013-09-18 Last modified: 2016-01-28

Priority: 3

View all other issues in [new.delete.placement].

View all issues with New status.

Discussion:

The library gives explicit permission in 16.4.5.2.1 [namespace.std] p2 that user code may explicitly instantiate a library template provided that the instantiations depend on at least one user-defined type:

> A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.

But it seems that the C++11 library is not specified in a way that guarantees such an instantiation to be well-formed if the minimum requirements of the library is not satisfied.

For example, in general, the first template parameter of `std::vector` is not required to be `DefaultConstructible` in general, but due to the split of the single C++03 member function with default argument

>     void resize(size_type sz, T c = T());

into

>     void resize(size_type sz);
>     void resize(size_type sz, const T& c);

the effect is now that for a type `ND` that is not `DefaultConstructible`, such as

>     struct NP {
>       NP(int);
>     };

the explicit instantiation of `std::vector<ND>` is no longer well-formed, because the attempt to instantiate the single-argument overload of `resize` cannot not succeed, because this function imposes the `DefaultInsertable` requirements and given the default allocator this effectively requires `DefaultConstructible`.

But `DefaultConstructible` is not the only point, what about `CopyConstructible` versus `MoveConstructible` alone? It turns out that currently the second `resize` overload would fail during an explicit instantiation for a type like

>     struct MO {
>       MO() = default;
>       MO(MO&&) = default;
>     };

because it imposes `CopyInsertable` requirements that end up being equivalent to the `CopyConstructible` requirements for the default allocator.

Technically a library can solve these issues: For special member functions by defining them in some base class, for others by transforming them effectively into a function template due to the great feature of default template arguments for function templates (At the very moment the validity of the latter approach depends on a resolution of core language issue CWG 1635, though). E.g. the here mentioned `resize` functions of `std::vector` could be prevented from instantiation by defining them like this with an implementation:

>     template<class = void>
>     void resize(size_type sz) { […] }
>     template<class = void>
>     void resize(size_type sz, const T& c) { […] }

In this case, these functions could also be defined in a base class, but the latter approach won't work in all cases.

Basically such an implementation is required to constrain all member functions that are not covered by the general requirements imposed on the actual library template parameters. I tested three different C++11 library implementations and but none could instantiate for example `std::list`, `std::vector`, or `std::deque` with value types that are not `DefaultConstructible` or only `MoveConstructible`.

This issue is raised to clarify the current situation in regard to the actual requirements imposed on user-provided types that are used to explicitly instantiate Library-provided templates. For example, the current Container requirements impose very little requirements on the actual value type and it is unclear to which extend library implementations have to respect that.

The minimum solution of this issue should be to at least realize that there is no fundamental requirement on `DefaultConstructible` for value types of library containers, because we have since C++03 the general statement of 16.4.4.2 [utility.arg.requirements] ("In general, a default constructor is not required."). It is unclear whether `CopyConstructible` should be required for an explicit instantiation request, but given the careful introduction of move operations in the library it would seem astonishing that a `MoveConstructible` type wouldn't suffice for value types of the container types.

In any case I can envision at least two approaches to solve this issue:

1. As indicated in LWG 2292(i), those function could get an explicit "Template Constraints:" element, albeit this promises more than needed to solve this issue.

2. The library could introduce a completely new element form, such as "Instantiation Constraints:" that would handle this situation for explicit instantiation situations. This would allow for simpler techniques to solve the issue when explicit instantiation is required compared to the first bullet, because it would not (necessarily) guarantee SFINAE-friendly expression-wellformedness, such as inspecting the expression `std::declval<std::vector<ND>&>.resize(0)` in an unevaluated context.

It should be noted that the 2013-08-27 comment to LWG 2193(i) could be resolved by a similar solution as indicated in this issue here.

Proposed resolution:

### 2307(i). Should the Standard Library use `explicit` only when necessary?

Section: 23 [containers] Status: LEWG Submitter: Zhihao Yuan Opened: 2013-09-26 Last modified: 2018-11-12

Priority: 2

View other active issues in [containers].

View all other issues in [containers].

View all issues with LEWG status.

Discussion:

LWG 2193(i) yields `explicit` for default ctors to allow `{}`, but not for all cases of uniform initialization. For example:

>     explicit vector(size_type count, const Allocator& alloc = Allocator());

This prevents `{n, alloc()}`. Although this use is relatively rare, but the behavior is inconsistent with that of

>     vector(size_type count, const T& value, const Allocator& alloc = Allocator());

[Urbana 2014-11-07: Move to Open]

[2018-08 Batavia Monday issue discussion]

This really needs a paper; splitting a lot of constructors. Nevin to write paper.

[2018-11 San Diego Thursday night issue processing]

LEWG has rejected Nevin's paper, so they need to formulate a policy.

Proposed resolution:

### 2321(i). Moving containers should (usually) be required to preserve iterators

Section: 23.2.2 [container.requirements.general] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2023-01-20

Priority: 3

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with Open status.

Discussion:

23.2.2 [container.requirements.general]/10 says that unless otherwise specified, "no `swap()` function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The `end()` iterator does not refer to any element, so it may be invalidated. — end note]". However, move constructors and move assignment operators aren't given similar invalidation guarantees. The guarantees need several exceptions, so I do not believe that blanket language like /11 "Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container." is applicable.

[2014-02-13 Issaquah]

General agreeement on intent, several wording nits and additional paragraphs to hit.

STL to provide updated wording. Move to Open.

[2015-02 Cologne]

AM: in the proposed wording, I'd like to mention that the iterators now refer to elements of a different container. I think we're saying something like this somewhere. JY: There's some wording like that for swap I think. TK: It's also in `list::splice()`. DK to JY: 23.2.1p9.

VV: The issue says that STL was going to propose new wording. Has he done that? AM: I believe we're looking at that. GR: The request touches on multiple paragraphs, and this PR has only one new paragraph, so this looks like it's not up-to-date. MC: This was last updated a year ago in Issaquah.

Conclusion: Skip, not up to date.

[2015-06, Telecon]

Still waiting for updated wording

[2015-08 Chicago]

Still waiting for updated wording

[2018-08-23 Batavia Issues processing]

Priority to 3

[2023-01-20; std-proposals post]

Emile Cormier observed that the proposed resolution of this issue contradicts with changes made by LWG 2839(i). Specifially, the current draft does not require container elements to be preserved on self-move-assignment. If this issue is accepted, it would either need to allow iterator invalidation on self-move-assignment or remove the "If `a` and `rv` do not refer to the same object" changes added to the container requirements by LWG 2839(i).

Proposed resolution:

This wording is relative to N3691.

1. In 23.2.2 [container.requirements.general]/10 change as indicated:

   > -10- Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.3.4, and 23.3.7.5) all container types defined in this Clause meet the following additional requirements:

   > - […]

   > - no copy constructor or assignment operator of a returned iterator throws an exception.

   > - no move constructor (or move assignment operator when `allocator_traits<allocator_type>::propagate_on_container_move_assignment::value` is true) of a container (except for `array`) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: The `end()` iterator does not refer to any element, so it may be invalidated. — end note]

   > - no `swap()` function throws an exception.

   > - no `swap()` function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The `end()` iterator does not refer to any element, so it may be invalidated. — end note]

### 2331(i). `regex_constants::collate`'s effects are inaccurately summarized

Section: 28.6.4.2 [re.synopt] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2016-01-28

Priority: 3

View all other issues in [re.synopt].

View all issues with Open status.

Discussion:

The table in 28.6.4.2 [re.synopt]/1 says that `regex_constants::collate` "Specifies that character ranges of the form "`[a-b]`" shall be locale sensitive.", but 28.6.12 [re.grammar]/14 says that it affects individual character comparisons too.

[2012-02-12 Issaquah : recategorize as P3]

Marshall Clow: 28.13/14 only applies to ECMAScript

All: we're unsure

Jonathan Wakely: we should ask John Maddock

Move to P3

[2014-5-14, John Maddock response]

The original intention was the original wording: namely that `collate` only made character ranges locale sensitive. To be frank it's a feature that's probably hardly ever used (though I have no real hard data on that), and is a leftover from early POSIX standards which required locale sensitive collation for character ranges, and then later changed to implementation defined if I remember correctly (basically nobody implemented locale-dependent collation).

So I guess the question is do we gain anything by requiring all character-comparisons to go through the locale when this bit is set? Certainly it adds a great deal to the implementation effort (it's not what Boost.Regex has ever done). I guess the question is are differing code-points that collate identically an important use case? I guess there might be a few Unicode code points that do that, but I don't know how to go about verifying that.

STL:

If this was unintentional, then 28.6.4.2 [re.synopt]/1's table should be left alone, while 28.6.12 [re.grammar]/14 should be changed instead.

Jeffrey Yasskin:

This page mentions that [V] in Swedish should match "W" in a perfect world.

However, the most recent version of TR18 retracts both language-specific loose matches and language-specific ranges because "for most full-featured regular expression engines, it is quite difficult to match under code point equivalences that are not 1:1" and "tailored ranges can be quite difficult to implement properly, and can have very unexpected results in practice. For example, languages may also vary whether they consider lowercase below uppercase or the reverse. This can have some surprising results: [a-Z] may not match anything if `Z < a` in that locale."

ECMAScript doesn't include collation at all.

IMO, +1 to changing 28.13 instead of 28.5.1. It seems like we'd be on fairly solid ground if we wanted to remove `regex_constants::collate` entirely, in favor of named character classes, but of course that's not for this issue.

Proposed resolution:

This wording is relative to N3691.

1. In 28.6.4.2 [re.synopt]/1, Table 138 — "`syntax_option_type` effects", change as indicated:

   > Table 138 — `syntax_option_type` effects
   > Element | Effect(s) if set
   > --- | ---
   > `…`
   > `collate` | Specifies that character ranges of the form "`[a-b]`"comparisons and character range comparisons shall be locale sensitive.
   > `…`

### 2338(i). §[re.traits]/7 expects of locale facets something not guaranteed by [locale.facet]/4

Section: 28.6.6 [re.traits], 28.3.3.1.2.2 [locale.facet] Status: Open Submitter: Sergey Zubkov Opened: 2013-10-15 Last modified: 2025-03-06

Priority: 3

View all other issues in [re.traits].

View all issues with Open status.

Discussion:

28.6.6 [re.traits]/7, begins with "if `typeid(use_facet<collate<charT> >) == typeid(collate_byname<charT>)`", which appears to be pseudocode with the intention to convey that the collate facet has not been replaced by the user. Cf. the wording in N1429 "there is no portable way to implement `transform_primary` in terms of `std::locale`, since even if the sort key format returned by `std::collate_byname<>::transform` is known and can be converted into a primary sort key, the user can still install their own custom `std::collate` implementation into the locale object used, and that can use any sort key format they see fit.".

Taken literally, 28.6.6 [re.traits]/7 appears to imply that named locales are required to hold their collate facets with dynamic type `std::collate_byname<charT>`, which is in fact true in some implementations (e.g libc++), but not others (e.g. libstdc++). This does not follow from the description of `_byname` in 28.3.3.1.2.2 [locale.facet]/4, which is only required to provide equivalent semantics, to the named locale's facet, not to actually be one.

[2015-05-06 Lenexa: Move to Open]

MC, RP: Consequence of failing to follow the rule is UB.

MC: Tightening of requirements.

RP: It should be this way, we just didn't impose it before.

MC: Second change is a bug fix, original code didn't work.

TK: Doesn't seem to make things worse.

Bring up in larger group tomorrow.

JW arrives.

JW: libstdc++ violates this due to two std::string ABIs.

JW: This prevents installing a type derived from Facet_byname, constrains the implementor from using a smarter derived class version.

JW: Can't look at facet id to detect replacement, because replacements have the same id.

RP: Can you give it multiple ids through multiple inheritance?

JW: No, the facet mechanism wouldn't like that.

JW: We should also ask Martin Sebor, he's implemented this stuff recently.

MC: Sounds like this resolution doesn't work, need a better solution.

JW: Write in words "if the facet has not been replaced by the user", the implementation knows how to detect that, but not like this.

RP: User RE traits need to detect this too.

JW: =(

Move to Open, JW will invite Martin Sebor to join LWG for discussion.

Later ...

JW: This is not needed for user specializations after all.

MC: Agree, [re.traits]/7 only applies to the stdlib traits.

NM: Effects: doesn't make sense.

JW, NM, Martin Sebor to come up with new wording.

[2025-03-06; Jonathan comments]

LWG 4186(i) fixed the missing `(getloc())` part. Maybe we can resolve this differently, by changing 28.6.6 [re.traits] to:

> Let `C` be `use_facet<collate<charT> >(getloc())`. If `typeid(C) == typeid(collate_byname<charT>) || typeid(C) == typeid(collate<charT>)` and the form of ...

Proposed resolution:

This wording is relative to N3691.

1. Modify 28.3.3.1.2.2 [locale.facet]/4 as indicated:

   > For some standard facets a standard "...`_byname`" class, derived from it, implements the virtual function semantics equivalent toprovided by that facet of the locale constructed by `locale(const char*)` with the same name. Each such facet provides a constructor that takes a `const char*` argument, which names the locale, and a `refs` argument, which is passed to the base class constructor. Each such facet also provides a constructor that takes a string argument `str` and a `refs` argument, which has the same effect as calling the first constructor with the two arguments `str.c_str()` and `refs`. If there is no "...`_byname`" version of a facet, the base class implements named locale semantics itself by reference to other facets. For any locale `loc` constructed by `locale(const char*)` and facet `Facet` that has a corresponding standard `Facet_byname` class, `typeid(use_facet<Facet>(loc)) == typeid(Facet_byname)`.

2. Modify 28.6.6 [re.traits]/7 as indicated:

   >     template <class ForwardIterator>
   >       string_type transform_primary(ForwardIterator first, ForwardIterator last) const;

   > > -7- Effects: if `typeid(use_facet<collate<charT> >(getloc())) == typeid(collate_byname<charT>)` and the form of the sort key returned by `collate_byname<charT>::transform(first, last)` is known and can be converted into a primary sort key then returns that key, otherwise returns an empty string.

### 2342(i). User conversion to `wchar_t const*` or to `wchar_t` not invoked for `operator<<`

Section: 31.7.6.2 [ostream] Status: New Submitter: Alf P. Steinbach Opened: 2013-10-29 Last modified: 2016-01-28

Priority: 4

View all other issues in [ostream].

View all issues with New status.

Discussion:

For wide streams argument types `wchar_t const*` and `wchar_t` are supported only as template parameters. User defined conversions are not considered for template parameter matching. Hence inappropriate overloads of `operator<<` are selected when an implicit conversion is required for the argument, which is inconsistent with the behavior for `char const*` and `char`, is unexpected, and is a useless result.

Demonstration:

>     #include <iostream>

>     struct Byte_string
>     {
>       operator char const*() const { return "Hurray, it works!"; }
>     };

>     struct Wide_string
>     {
>       operator wchar_t const*() const { return L"Hurray, it works!"; }
>     };

>     struct Byte_ch
>     {
>       operator char() const { return 'X'; }
>     };

>     struct Wide_ch
>     {
>       operator wchar_t() const { return L'X'; }
>     };

>     auto main() -> int
>     {
>       using namespace std;
>       wcout << "'X' as char value   : " << Byte_ch() << endl;
>       wcout << "'X' as wchar_t value: " << Wide_ch() << endl;
>       wcout << "Byte string pointer : " << Byte_string() << endl;
>       wcout << "Wide string pointer : " << Wide_string() << endl;
>     }

Example output:

>     'X' as char value   : X
>     'X' as wchar_t value: 88
>     Byte string pointer : Hurray, it works!
>     Wide string pointer : 000803C8

Proposed resolution:

This wording is relative to N3797.

1. Modify 31.7.6.2 [ostream], class template `basic_ostream` synopsis, as indicated:

   >     namespace std {
   >     […]

   >     // 27.7.3.6.4 character inserters
   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
   >                                               charT);
   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
   >                                               char);
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
   >                                              char);
   >     template<class traits>
   >       basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&,
   >                                                 wchar_t);
   >     […]

   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
   >                                               const charT*);
   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
   >                                               const char*);
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
   >                                              const char*);
   >     template<class traits>
   >       basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&,
   >                                                 const wchar_t*);
   >     […]
   >     }

2. Modify 31.7.6.3.4 [ostream.inserters.character] as indicated: [Drafting note: The replacement of `os` by `out` in p1 and the insertion of "`out.`" in p4 just fix two obvious typos — end drafting note]

   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
   >                                               charT c);
   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
   >                                               char c);
   >     // specialization
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                              char c);
   >     template<class traits>
   >       basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out,
   >                                                 wchar_t c);

   >     // signed and unsigned
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                               signed char c);
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                               unsigned char c);

   > > -1- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of `out`. Constructs a character sequence `seq`. If `c` has type `char` and the character type of the stream is not `char`, then `seq` consists of `out.widen(c)`; otherwise `seq` consists of `c`. Determines padding for `seq` as described in 31.7.6.3.1 [ostream.formatted.reqmts]. Inserts `seq` into `out`. Calls `osout.width(0)`.

   > > -2- Returns: `out`.

   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
   >                                               const charT* s);
   >     template<class charT, class traits>
   >       basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
   >                                               const char* s);
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                              const char* s);
   >     template<class traits>
   >       basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out,
   >                                                 const wchar_t* s);

   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                              const signed char* s);
   >     template<class traits>
   >       basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
   >                                              const unsigned char* s);

   > > -3- Requires: `s` shall not be a null pointer.

   > > -4- Effects: Behaves like a formatted inserter (as described in 31.7.6.3.1 [ostream.formatted.reqmts]) of `out`. Creates a character sequence `seq` of `n` characters starting at `s`, each widened using `out.widen()` (27.5.5.3), where `n` is the number that would be computed as if by:

   > > - `traits::length(s)` for the following overloads:

   > >   - where the first argument is of type `basic_ostream<charT, traits>&` and the second is of type `const charT*`,

   > >   - and also for the overload where the first argument is of type `basic_ostream<char, traits>&` and the second is of type `const char*`,

   > >   - where the first argument is of type `basic_ostream<wchar_t, traits>&` and the second is of type `const wchar_t*`,

   > > - `std::char_traits<char>::length(s)` for the overload where the first argument is of type `basic_ostream<charT, traits>&` and the second is of type `const char*`,

   > > - `traits::length(reinterpret_cast<const char*>(s))` for the other two overloads.

   > > Determines padding for `seq` as described in 31.7.6.3.1 [ostream.formatted.reqmts]. Inserts `seq` into `out`. Calls `out.width(0)`.

   > > -5- Returns: `out`.

### 2348(i). `charT('1')` is not the wide equivalent of `'1'`

Section: 22.9.2 [template.bitset], 31.7.9 [quoted.manip] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-02 Last modified: 2016-01-28

Priority: 3

View all other issues in [template.bitset].

View all issues with Open status.

Discussion:

Example: `char16_t('1') != u'1'` is possible.

The numeric value of `char16_t` is defined to be Unicode code point, which is same to the ASCII value and UTF-8 for 7-bit chars. However, `char` is not guaranteed to have an encoding which is compatible with ASCII. For example, `'1'` in EBCDIC is 241.

I found three places in the standard casting narrow char literals: `bitset::bitset`, `bitset::to_string` and `quoted`.

PJ confirmed this issue and says he has a solution used in their `<filesystem>` implementation, and he may want to propose it to the standard.

The solution in my mind, for now, is to make those default arguments magical, where the "magic" can be implemented with a C11 `_Generic` selection (works in clang):

>     #define _G(T, literal) _Generic(T{}, \
>           char: literal, \
>           wchar_t: L ## literal, \
>           char16_t: u ## literal, \
>           char32_t: U ## literal)

>       _G(char16_t, '1') == u'1'

[Lenexa 2015-05-05: Move to Open]

Ask for complete PR (need quoted, to string, et al.)

Will then take it up again

Expectation is that this is correct way to fix this

Proposed resolution:

This wording is relative to N3797.

[Drafting note: This is a sample wording fixing only one case; I'm just too lazy to copy-paste it before we discussed whether the solution is worth and sufficient (for example, should the other `charT`s like `unsigned char` just don't compile without supplying those arguments? I hope so). — end drafting note]

1. Modify 22.9.2 [template.bitset] p1, class template `bitset` synopsis, as indicated:

   >     namespace std {
   >       template <size_t N> class bitset {
   >       public:
   >         […]
   >         template<class charT, class traits, class Allocator>
   >           explicit bitset(
   >             const basic_string<charT,traits,Allocator>& str,
   >             typename basic_string<charT,traits,Allocator>::size_type pos = 0,
   >             typename basic_string<charT,traits,Allocator>::size_type n =
   >               basic_string<charT,traits,Allocator>::npos,
   >               charT zero = charT('0')see below, charT one = charT('1')see below);
   >          […]
   >       };
   >       […]
   >     }

2. Modify 22.9.2.2 [bitset.cons] as indicated:

   >     template<class charT, class traits, class Allocator>
   >     explicit
   >     bitset(const basic_string<charT, traits, Allocator>& str,
   >            typename basic_string<charT, traits, Allocator>::size_type pos = 0,
   >            typename basic_string<charT, traits, Allocator>::size_type n =
   >              basic_string<charT, traits, Allocator>::npos,
   >              charT zero = charT('0')see below, charT one = charT('1')see below);

   > > -?- The default values of `zero` and `one` compare equal to the character literals `0` and `1` of type `charT`, respectively.

   > > -3- Requires:: `pos <= str.size()`.

   > > […]

### 2358(i). Apparently-bogus definition of `is_empty` type trait

Section: 21.3.5.4 [meta.unary.prop] Status: Open Submitter: Richard Smith Opened: 2014-02-01 Last modified: 2017-02-02

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with Open status.

Discussion:

The 'Condition' for `std::is_empty` is listed as:

> "`T` is a class type, but not a union type, with no non-static data members other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base class `B` for which `is_empty<B>::value` is false."

This is incorrect: there is no such thing as a non-static data member that is a bit-field of length 0, since bit-fields of length 0 must be unnamed, and unnamed bit-fields are not members (see 11.4.10 [class.bit] p2).

It also means that classes such as:

>     struct S {
>      int : 3;
>     };

are empty (because they have no non-static data members). There's implementation divergence on the value of `is_empty<S>::value`.

I'm not sure what the purpose of `is_empty` is (or how it could be useful), but if it's desirable for the above type to not be treated as empty, something like this could work:

> "`T` is a class type, but not a union type, with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class `B` for which `is_empty<B>::value` is false."

and if the above type should be treated as empty, then this might be appropriate:

> "`T` is a class type, but not a union type, with no (named) non-static data members other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base class `B` for which `is_empty<B>::value` is false."

[2016-08 Chicago]

Walter says: We want `is_empty_v<S>` to produce false as a result. Therefore, we recommend adoption of the first of the issue's suggestions.

Tuesday AM: Moved to Tentatively Ready

Previous resolution [SUPERSEDED]:

[2016-10 by Marshall - this PR incorrectly highlighted changed portions]

> Modify Table 38 — Type property predicates for `is_empty` as follows:

> > `T` is a non-union class type with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class `B` for which `is_empty_v<B>` is false.

[2016-10 Telecon]

Should probably point at section 1.8 for some of this. Status back to 'Open'

Proposed resolution:

Modify Table 38 — Type property predicates for `is_empty` as follows:

> `T` is a class type, but not a union type,is a non-union class type with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class `B` for which `is_empty_v<B>` is false.

### 2362(i). unique, associative `emplace()` should not move/copy the `mapped_type` constructor arguments when no insertion happens

Section: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: New Submitter: Jeffrey Yasskin Opened: 2014-02-15 Last modified: 2015-09-23

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with New status.

Discussion:

`a_uniq.emplace(args)` is specified as:

> Effects: Inserts a value_type object `t` constructed with
> `std::forward<Args>(args)...` if and only if there is no element in the
> container with key equivalent to the key of `t`. The `bool` component of
> the returned pair is true if and only if the insertion takes place,
> and the iterator component of the pair points to the element with key
> equivalent to the key of `t`.

However, we occasionally find code of the form:

>     std::unique_ptr<Foo> p(new Foo);
>     auto res = m.emplace("foo", std::move(p));

where we'd like to avoid destroying the `Foo` if the insertion doesn't take place (if the container already had an element with the specified key).

N3873 includes a partial solution to this in the form of a new `emplace_stable` member function, but LEWG's discussion strongly agreed that we'd rather have `emplace()` Just Work:

Should `map::emplace()` be guaranteed not to move/copy its arguments if the insertion doesn't happen?

SF: 8 F: 3 N: 0 A: 0 SA: 0

This poll was marred by the fact that we didn't notice or call out that `emplace()` must construct the key before doing the lookup, and it must not then move the key after it determines whether an insert is going to happen, and the `mapped_type` instance must live next to the key.

The very similar issue 2006(i) was previously marked NAD, with N3178 as discussion. However, given LEWG's interest in the alternate behavior, we should reopen the question in this issue.

We will need a paper that describes how to implement this before we can make more progress.

Proposed resolution:

### 2366(i). `istreambuf_iterator` end-of-stream equality

Section: 24.6.4 [istreambuf.iterator] Status: New Submitter: Hyman Rosen Opened: 2014-02-19 Last modified: 2023-04-13

Priority: 3

View other active issues in [istreambuf.iterator].

View all other issues in [istreambuf.iterator].

View all issues with New status.

Discussion:

Given the following code,

>     #include <sstream>

>     std::stringbuf buf;
>     std::istreambuf_iterator<char> begin(&buf);
>     std::istreambuf_iterator<char> end;

it is not clear from the wording of the Standard whether `begin.equal(end)` must be true. In at least one implementation it is not (CC: Sun C++ 5.10 SunOS_sparc Patch 128228-25 2013/02/20) and in at least one implementation it is (gcc version 4.3.2 x86_64-unknown-linux-gnu).

24.6.4 [istreambuf.iterator] says that `end` is an end-of-stream iterator since it was default constructed. It also says that an iterator becomes equal to an end-of-stream iterator when end of stream is reached by `sgetc()` having returned `eof()`. [istreambuf.iterator::equal] says that `equal()` returns true iff both iterators are end of stream or not end of stream. But there seems to be no requirement that `equal` check for end-of-stream by calling `sgetc()`.

Jiahan Zi at BloombergLP discovered this issue through his code failing to work correctly. Dietmar Kühl has opined in a private communication that the iterators should compare equal.

[2023-03-31; Jonathan Wakely comments]

I agree that they should compare equal, but that's in conflict with the resolution of LWG 2544(i), which says that `begin` must not be at end-of-stream because `&buf` is not null.

[2023-04-12; Jonathan adds wording]

Proposed resolution:

This wording is relative to N4944.

1. Change 24.6.4.1 [istreambuf.iterator.general] as indicated:

   >         constexpr istreambuf_iterator() noexcept;
   >         constexpr istreambuf_iterator(default_sentinel_t) noexcept;
   >         istreambuf_iterator(const istreambuf_iterator&) noexcept = default;
   >         ~istreambuf_iterator() = default;
   >         istreambuf_iterator(istream_type& s) noexcept;
   >         : istreambuf_iterator(s.rdbuf()) { }
   >         istreambuf_iterator(streambuf_type* s) noexcept;
   >         istreambuf_iterator(const proxy& p) noexcept;
   >         …

   >       private:
   >         streambuf_type* sbuf_;              // exposition only
   >         int_type c_{};                      // exposition only
   >       };
   >     }

2. Change 24.6.4.3 [istreambuf.iterator.cons] as indicated:

   > For each `istreambuf_iterator` constructor in this section, an end-of-stream iterator is constructed if and only if the exposition-only member `sbuf_` is initialized with a null pointer value or if `sbuf_->sgetc()` returns `traits_type::eof()`.

   >     constexpr istreambuf_iterator() noexcept;
   >     constexpr istreambuf_iterator(default_sentinel_t) noexcept;

   > > -1- Effects: Initializes `sbuf_` with `nullptr`.

   >     istreambuf_iterator(istream_type& s) noexcept;

   > > -2- Effects: Initializes `sbuf_` with `s.rdbuf()`.

   >     istreambuf_iterator(streambuf_type* s) noexcept;

   > > [Drafting note: `sgetc()` can throw, but this function is `noexcept`. Should it swallow exceptions and create an end-of-stream iterator, to avoid weakening the exception spec of an existing function?]

   > > -3- Effects: Initializes `sbuf_` with `s`. If `s` is not null, initializes `c_` with `s->sgetc()`. Sets `sbuf_` to null if `sgetc` exits via an exception, or if `traits_type::eq_int_type(c_, traits_type::eof())` is `true`.

   >     istreambuf_iterator(const proxy& p) noexcept;

   > > -4- Effects: Initializes `sbuf_` with `p.sbuf_`. If `p.sbuf_` is not null, initializes `c_` with `p.keep_`.

3. Change 24.6.4.4 [istreambuf.iterator.ops] as indicated:

   >     charT operator*() const;

   > > -?- Preconditions: `sbuf_` is not null.

   > > -1- Returns: The character obtained via the `streambuf` member `sbuf_->sgetc()`. `traits_type::to_char_type(c_)`.

   > > -?- Throws: Nothing.

   >     istreambuf_iterator& operator++();

   > > -?- Preconditions: `sbuf_` is not null.

   > > -2- Effects: As if by `sbuf_->sbumpc()`. Performs `c_ = sbuf_->snextc()`, then sets `sbuf_` to null if `traits_type::eq_int_type(c_, traits_type::eof())` is `true`.

   > > -3- Returns: `*this`.

   >     proxy operator++(int);

   > > -4- Returns: `proxy(sbuf_->sbumpc(), sbuf_)`.
   > > Effects: Equivalent to:

   > >     proxy p(**this, sbuf_);
   > >     ++*this;
   > >     return p;

   >     bool equal(const istreambuf_iterator& b) const;

   > > -5- Returns: `bool(sbuf_) == bool(b.sbuf_)`.

   > > [Note: This is `true` if and only if both iterators are at end-of-stream, or neither is at end-of-stream, regardless of what streambuf object they use. — end note]

   >     template<class charT, class traits>
   >       bool operator==(const istreambuf_iterator<charT, traits>& a,
   >                       const istreambuf_iterator<charT, traits>& b);

   > > -6- Returns: `a.equal(b)`.

   >     bool equal(const istreambuf_iterator& i, default_sentinel_t s) const;

   > > -7- Returns: `i.equal(s) i.sbuf_ == nullptr`.

### 2383(i). Overflow cannot be ill-formed for chrono::duration integer literals

Section: 30.5.9 [time.duration.literals] Status: Open Submitter: Jonathan Wakely Opened: 2014-05-16 Last modified: 2014-11-08

Priority: 3

View all issues with Open status.

Discussion:

30.5.9 [time.duration.literals] p3 says:

> If any of these suffixes are applied to an integer literal and the resulting `chrono::duration` value cannot be represented in the result type because of overflow, the program is ill-formed.

Ill-formed requires a diagnostic at compile-time, but there is no way to detect the overflow from `unsigned long long` to the `signed duration<>::rep` type.

Overflow could be detected if the duration integer literals were literal operator templates, otherwise overflow can either be undefined or a run-time error, not ill-formed.

[Urbana 2014-11-07: Move to Open]

Proposed resolution:

### 2398(i). `type_info`'s destructor shouldn't be required to be virtual

Section: 17.7.3 [type.info] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-06-14 Last modified: 2016-08-06

Priority: 3

View all other issues in [type.info].

View all issues with Open status.

Discussion:

`type_info`'s destructor is depicted as being `virtual`, which is nearly unobservable to users (since they can't construct or copy this class, they can't usefully derive from it). However, it's technically observable (via `is_polymorphic` and `has_virtual_destructor`). It also imposes real costs on implementations, requiring them to store one vptr per `type_info` object, when RTTI space consumption is a significant concern.

Making this implementation-defined wouldn't affect users (who can observe this only if they're specifically looking for it) and wouldn't affect implementations who need `virtual` here, but it would allow other implementations to drop `virtual` and improve their RTTI space consumption.

Richard Smith:

It's observable in a few other ways.

>     std::map<void*, something> m;
>     m[dynamic_cast<void*>(&typeid(blah))] = stuff;

... is broken by this change, because you can't `dynamic_cast` a non-polymorphic class type to `void*`.

>     type_info& f();
>     typeid(f());

... evaluates `f()` at runtime without this change, and might not do so with this change.

These are probably rare things, but I can imagine at least some forms of the latter being used in SFINAE tricks.

[Lenexa 2015-05-05: Move to Open]

Marshall to poll LEWG for their opinion

[2016-06]

On the reflector, STL wrote:

> We'll prototype this change and report back with data in the future.

[2016-08 Chicago]

No update from STL. Set priority to P3

Proposed resolution:

This wording is relative to N3936.

1. Change 17.7.3 [type.info] as indicated:

   >     namespace std {
   >       class type_info {
   >       public:
   >         virtualsee below ~type_info();
   >         […]
   >       };
   >     }

   > -1- The class `type_info` describes type information generated by the implementation. Objects of this class effectively store a pointer to a name for the type, and an encoded value suitable for comparing two types for equality or collating order. The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs. Whether `~type_info()` is `virtual` is implementation-defined.

### 2421(i). Non-specification of handling zero size in `std::align` [ptr.align]

Section: 20.2.5 [ptr.align] Status: New Submitter: Melissa Mears Opened: 2014-08-06 Last modified: 2014-11-03

Priority: 3

View all other issues in [ptr.align].

View all issues with New status.

Discussion:

The specification of `std::align` does not appear to specify what happens when the value of the `size` parameter is 0. (The question of what happens when `alignment` is 0 is mentioned in another Defect Report, 2377(i); it would change the behavior to be undefined rather than potentially implementation-defined.)

The case of `size` being 0 is interesting because the result is ambiguous. Consider the following code's output:

>     #include <cstdio>
>     #include <memory>

>     int main()
>     {
>       alignas(8) char buffer[8];
>       void *ptr = &buffer[1];
>       std::size_t space = sizeof(buffer) - sizeof(char[1]);

>       void *result = std::align(8, 0, ptr, space);

>       std::printf("%d %td\n", !!result, result ? (static_cast<char*>(result) - buffer) : std::ptrdiff_t(-1));
>     }

There are four straightforward answers as to what the behavior of `std::align` with size 0 should be:

1. The behavior is undefined because the size is invalid.

2. The behavior is implementation-defined. This seems to be the status quo, with current implementations using #3.

3. Act the same as `size == 1`, except that if `size == 1` would fail but would be defined and succeed if space were exactly 1 larger, the result is a pointer to the byte past the end of the `ptr` buffer. That is, the "aligned" version of a 0-byte object can be one past the end of an allocation. Such pointers are, of course, valid when not dereferenced (and a "0-byte object" shouldn't be), but whether that is desired is not specified in the Standard's definition of `std::align`, it appears. The output of the code sample is "`1 8`" in this case.

4. Act the same as `size == 1`; this means that returning "one past the end" is not a possible result. In this case, the code sample's output is "`0 -1`".

The two compilers I could get working with `std::align`, Visual Studio 2013 and Clang 3.4, implement #3. (Change `%td` to `%Id` on Visual Studio 2013 and earlier. 2014 and later will have `%td`.)

Proposed resolution:

### 2423(i). Missing specification `slice_array`, `gslice_array`, `mask_array`, `indirect_array` copy constructor

Section: 29.6.5 [template.slice.array], 29.6.7 [template.gslice.array], 29.6.8 [template.mask.array], 29.6.9 [template.indirect.array] Status: New Submitter: Akira Takahashi Opened: 2014-08-12 Last modified: 2014-11-03

Priority: 4

View all other issues in [template.slice.array].

View all issues with New status.

Discussion:

I found a missing specification of the copy constructor of the following class templates:

- `slice_array` (29.6.5 [template.slice.array])

- `gslice_array` (29.6.7 [template.gslice.array])

- `mask_array` (29.6.8 [template.mask.array])

- `indirect_array` (29.6.9 [template.indirect.array])

Proposed resolution:

1. Before 29.6.5.2 [slice.arr.assign] insert a new sub-clause as indicated:

   -?- `slice_array` constructors [slice.arr.cons]

   >     slice_array(const slice_array&);

   > -?- Effects: The constructed slice refers to the same `valarray<T>` object to which the argument slice refers.

2. Before 29.6.7.2 [gslice.array.assign] insert a new sub-clause as indicated:

   -?- `gslice_array` constructors [gslice.array.cons]

   >     gslice_array(const gslice_array&);

   > -?- Effects: The constructed slice refers to the same `valarray<T>` object to which the argument slice refers.

3. Before 29.6.8.2 [mask.array.assign] insert a new sub-clause as indicated:

   -?- `mask_array` constructors [mask.array.cons]

   >     mask_array(const mask_array&);

   > -?- Effects: The constructed slice refers to the same `valarray<T>` object to which the argument slice refers.

4. Before 29.6.9.2 [indirect.array.assign] insert a new sub-clause as indicated:

   -?- `indirect_array` constructors [indirect.array.cons]

   >     indirect_array(const indirect_array&);

   > -?- Effects: The constructed slice refers to the same `valarray<T>` object to which the argument slice refers.

### 2431(i). Missing regular expression traits requirements

Section: 28.6.2 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2014-09-30 Last modified: 2020-04-16

Priority: 3

View other active issues in [re.req].

View all other issues in [re.req].

View all issues with New status.

Discussion:

The requirements on the traits class in 28.6.2 [re.req] do not say whether a regular expression traits class is required to be `DefaultConstructible`, `CopyConstructible`, `CopyAssignable` etc.

The `std::regex_traits` class appears to be all of the above, but can `basic_regex` assume that for user-defined traits classes?

Should the following statements all leave `u` in equivalent states?

>     X u{v};
>     X u; u = v;
>     X u; u.imbue(v.getloc();

Whether they are equivalent has implications for `basic_regex` copy construction and assignment.

[2020-04-16, Jonathan adds that 28.6.7.5 [re.regex.locale] requires the traits type to be default-initialized, despite no guarantee that the traits type is default constructible. ]

Proposed resolution:

### 2452(i). `is_constructible`, etc. and default arguments

Section: 21 [meta] Status: Core Submitter: Hubert Tong Opened: 2014-11-04 Last modified: 2015-10-21

Priority: 3

View other active issues in [meta].

View all other issues in [meta].

View all issues with Core status.

Discussion:

The `BaseCharacteristic` for `is_constructible` is defined in terms of the well-formedness of a declaration for an invented variable. The well-formedness of the described declaration itself may change for the same set of arguments because of the introduction of default arguments.

In the following program, there appears to be conflicting definitions of a specialization of `std::is_constructible`; however, it seems that this situation is caused without a user violation of the library requirements or the ODR. There is a similar issue with `is_convertible`, `result_of` and others.

a.cc:

>     #include <type_traits>
>     struct A { A(int, int); };
>     const std::false_type& x1 = std::is_constructible<A, int>();

>     int main() { }

b.cc:

>     #include <type_traits>
>     struct A { A(int, int); };

>     inline A::A(int, int = 0) { }

>     const std::true_type& x2 = std::is_constructible<A, int>();

Presumably this program should invoke undefined behaviour, but the Library specification doesn't say that.

[2015-02 Cologne]

Core wording should say "this kind of thing is ill-formed, no diagnostic required"

Proposed resolution:

### 2453(i). §[iterator.range] and now [iterator.container] aren't available via `<initializer_list>`

Section: 17.11 [support.initlist], 24.7 [iterator.range] Status: New Submitter: Richard Smith Opened: 2014-11-11 Last modified: 2021-06-06

Priority: 3

View other active issues in [support.initlist].

View all other issues in [support.initlist].

View all issues with New status.

Discussion:

These sections define helper functions, some of which apply to `initializer_list<T>`. And they're available if you include one of a long list of header files, many of which include `<initializer_list>`. But they are not available if you include `<initializer_list>`. This seems very odd.

>     #include <initializer_list>
>     auto x = {1, 2, 3};
>     const int *p = data(x); // error, undeclared
>     #include <vector>
>     const int *q = data(x); // ok

Proposed resolution:

### 2461(i). Interaction between allocators and container exception safety guarantees

Section: 16.4.4.6 [allocator.requirements], 23.3.13.3 [vector.capacity], 23.3.13.5 [vector.modifiers] Status: New Submitter: dyp Opened: 2014-12-06 Last modified: 2015-06-10

Priority: 3

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

When resizing a `vector`, the accessibility and exception specification of the value type's constructors determines whether the elements are copied or moved to the new buffer. However, the copy/move is performed via the allocator's `construct` member function, which is assumed, but not required, to call the copy/move constructor and propagate only exceptions from the value type's copy/move constructor. The issue might also affect other classes.

The current wording in N4296 relevant here is from Table 28 — "Allocator requirements" in 16.4.4.6 [allocator.requirements]:

> Table 28 — Allocator requirements
> Expression | Return type | Assertion/note pre-/post-condition | Default
> --- | --- | --- | ---
> `…`
> `a.construct(c, args)` | (not used) | Effect: Constructs an object of type `C` at `c` | `::new ((void*)c) C(forward<Args>(args)...)`
> `…`

and from 16.4.4.6 [allocator.requirements] p9:

> An allocator may constrain the types on which it can be instantiated and the arguments for which its `construct` member may be called. If a type cannot be used with a particular allocator, the allocator class or the call to `construct` may fail to instantiate.

I conclude the following from the wording:

1. The allocator is not required to call the copy constructor if the arguments (args) is a single (potentially const) lvalue of the value type. Similarly for a non-const rvalue + move constructor. See also 23.2.2 [container.requirements.general] p15 which seems to try to require this, but is not sufficient: That paragraph specifies the semantics of the allocator's operations, but not which constructors of the value type are used, if any.

2. The allocator may throw exceptions in addition to the exceptions propagated by the constructors of the value type; it can also propagate exceptions from constructors other than a copy/move constructor.

This leads to an issue with the wording of the exception safety guarantees for vector modifiers in 23.3.13.5 [vector.modifiers] p1:

> […]

>     void push_back(const T& x);
>     void push_back(T&& x);

> > Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of `T` or by any InputIterator operation there are no effects. If an exception is thrown while inserting a single element at the end and `T` is `CopyInsertable` or `is_nothrow_move_constructible<T>::value` is true, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-`CopyInsertable` `T`, the effects are unspecified.

The wording leads to the following problem: Copy and move assignment are invoked directly from `vector`. For intermediary objects (see 2164(i)), `vector` also directly invokes the copy and move constructor of the value type. However, construction of the actual element within the buffer is invoked via the allocator abstraction. As discussed above, the allocator currently is not required to call a copy/move constructor. If `is_nothrow_move_constructible<T>::value` is `true` for some value type `T`, but the allocator uses modifying operations for `MoveInsertion` that do throw, the implementation is required to ensure that "there are no effects", even if the source buffer has been modified.

Similarly, the `vector` capacity functions specify exception safety guarantees referring to the move constructor of the value type. For example, `vector::resize` in 23.3.13.3 [vector.capacity] p14:

> Remarks: If an exception is thrown other than by the move constructor of a non-`CopyInsertable` `T` there are no effects.

The wording leads to the same issue as described above.

Code example:

>     template<class T>
>     class allocator;

>     class pot_reg_type // a type which creates
>                        // potentially registered instances
>     {
>     private:
>       friend class allocator<pot_reg_type>;
>       struct register_t {};

>       static std::set<pot_reg_type*>& get_registry()
>       {
>         static std::set<pot_reg_type*> registry;
>         return registry;
>       }
>       void enregister() noexcept(false)
>       {
>         get_registry().insert(this);
>       }
>       void deregister()
>       {
>         get_registry().erase(this);
>       }

>     public:
>       pot_reg_type(void               ) noexcept(true) {}
>       pot_reg_type(pot_reg_type const&) noexcept(true) {}
>       pot_reg_type(pot_reg_type&&     ) noexcept(true) {}

>     private:
>       pot_reg_type(register_t                     ) noexcept(false)
>       { enregister(); }
>       pot_reg_type(register_t, pot_reg_type const&) noexcept(false)
>       { enregister(); }
>       pot_reg_type(register_t, pot_reg_type&&     ) noexcept(false)
>       { enregister(); }
>     };

>     template<class T>
>     class allocator
>     {
>     public:
>       using value_type = T;

>       value_type* allocate(std::size_t p)
>       { return (value_type*) ::operator new(p); }

>       void deallocate(value_type* p, std::size_t)
>       { ::operator delete(p); }

>       void construct(pot_reg_type* pos)
>       {
>         new((void*)pos) pot_reg_type((pot_reg_type::register_t()));
>       }
>       void construct(pot_reg_type* pos, pot_reg_type const& source)
>       {
>         new((void*)pos) pot_reg_type(pot_reg_type::register_t(), source);
>       }

>       template<class... Args>
>       void construct(T* p, Args&&... args)
>       {
>         new((void*)p) T(std::forward<Args>(args)...);
>       }
>     };

The `construct` member function template is only required for rebinding, which can be required e.g. to store additional debug information in the allocated memory (e.g. VS2013).

Even though the value type has an accessible and `noexcept(true)` move constructor, this allocator won't call that constructor for rvalue arguments. In any case, it does not call a constructor for which vector has formulated its requirements. An exception thrown by a constructor called by this allocator is not covered by the specification in 23.3.13.5 [vector.modifiers] and therefore is guaranteed not to have any effect on the vector object when resizing.

For an example how this might invalidate the exception safety guarantees, see this post on the std-discussion mailing list.

Another problem arises for value types whose constructors are private, but may be called by the allocator e.g. via friendship. Those value types are not `MoveConstructible` (`is_move_constructible` is false), yet they can be `MoveInsertable`. It is not possible for `vector` to create intermediary objects (see 2164(i)) of such a type by directly using the move constructor. Current implementations of the single-element forms of `vector::insert` and `vector::emplace` do create intermediary objects by directly calling one of the value type's constructors, probably to allow inserting objects from references that alias other elements of the container. As far as I can see, Table 100 — "Sequence container requirements" in 23.2.4 [sequence.reqmts] does not require that the creation of such intermediare objects can be performed by containers using the value type's constructor directly. It is unclear to me if the allocator's construct function could be used to create those intermediary objects, given that they have not been allocated by the allocator.

Two possible solutions:

1. Add the following requirement to the `allocator_traits::construct` function: If the parameter pack `args` consists of a single parameter of the type `value_type&&`, the function may only propagate exceptions if `is_nothrow_move_constructible<value_type>::value` is `false`.

   Requiring `alloctor_traits::construct` to call a true copy/move constructor of the value type breaks `std::scoped_allocator_adapter`, as pointed out by Casey Carter in a post on the std-discussion mailing list.

2. Change vector's criterion whether to move or copy when resizing:

   Instead of testing the value type's constructors via `is_move_constructible`, check the value of `noexcept( allocator_traits<Allocator>::construct(alloc, ptr, rval) )` where `alloc` is an lvalue of type `Allocator`, `ptr` is an expression of type `allocator_traits<Allocator>::pointer` and `rval` is a non-const rvalue of type `value_type`.

A short discussion of the two solutions:

Solution 1 allows keeping `is_nothrow_move_constructible<value_type>` as the criterion for `vector` to decide between copying and moving when resizing. It restricts what can be done inside the `construct` member function of allocators, and requires implementers of allocators to pay attention to the value types used. One could conceive allocators checking the following with a `static_assert`: If the value type `is_nothrow_move_constructible`, then the constructor actually called for `MoveInsertion` within the `construct` member function is also declared as noexcept.

Solution 2 requires changing both the implementation of the default allocator (add a conditional `noexcept`) and `vector` (replace `is_move_constructible` with an allocator-targeted check). It does not impose additional restrictions on the allocator (other than 23.2.2 [container.requirements.general] p15), and works nicely even if the move constructor of a `MoveInsertable` type is private or deleted (the allocator might be a friend of the value type).

In both cases, an addition might be required to provide the basic exception safety guarantee. A short discussion on this topic can be found in the std-discussion mailing list. Essentially, if `allocator_traits<Allocator>::construct` throws an exception, the object may or may not have been constructed. Two solutions are mentioned in that discussion:

1. `allocator_traits<Allocator>::construct` needs to tell its caller whether or not the construction was successful, in case of an exception.

2. If `allocator_traits<Allocator>::construct` propagates an exception, it shall either not have constructed an object at the specified location, or that object shall have been destroyed (or it shall ensure otherwise that no resources are leaked).

[2015-05-23, Tomasz Kamiński comments]

Solution 1 discussed in this issue also breaks support for the `polymorphic_allocator` proposed in the part of the Library Fundamentals TS v1, in addition to already mentioned `std::scoped_allocator_adapter`. Furthermore there is unknown impact on the other user-defined state-full allocators code written in the C++11.

In addition the library resolution proposed in the LWG issues 2089(i) and N4462, will break the relation between the `std::allocator_trait::construct` method and copy/move constructor even for the standard `std::allocator`. As example please consider following class:

>     struct NonCopyable
>     {
>       NonCopyable() = default;
>       NonCopyable(NonCopyable const&) = delete;
>       NonCopyable(NonCopyable&&) = delete;
>     };

>     struct InitListConstructor : NonCopyable
>     {
>       InitListConstructor() = default;
>       InitListConstructor(std::initializer_list<int>);
>       operator int() const;
>     };

For the above declarations following expression are ill-formed:

>     InitListConstructor copy(std::declval<InitListConstructor const&>());
>     InitListConstructor move(std::declval<InitListConstructor&&>());

So the class is not `CopyConstructible` nor `MoveConstructible`. However the following are well formed:

>     InitListConstructor copy{std::declval<InitListConstructor const&>()};
>     InitListConstructor move{std::declval<InitListConstructor&&>()};

And will be used by `std::allocator<InitListConstructor>::construct` in case of move-insertion and copy-insertion, after appliance of the resolution proposed in mentioned papers:

> The gist of the proposed library fix is simple:

> - if `is_constructible_v<TargetType, Args...>`, use direct-nonlist-initialization

> - otherwise, use brace-initialization.

As consequence the requirement proposed in the Solution 1:

> If the parameter pack `args` consists of a single parameter of the type `value_type&&`, the function may only propagate exceptions if `is_nothrow_move_constructible<value_type>::value` is false.

Will no longer hold for the `std::allocator`.

Proposed resolution:

### 2471(i). `copy_n`'s number of `InputIterator` increments unspecified

Section: 26.7.1 [alg.copy] Status: Open Submitter: Jonathan Wakely Opened: 2015-01-28 Last modified: 2018-06-22

Priority: 3

View other active issues in [alg.copy].

View all other issues in [alg.copy].

View all issues with Open status.

Discussion:

It's unspecified how many times `copy_n` increments the `InputIterator`. `uninitialized_copy_n` is specified to increment it exactly `n` times, which means if an `istream_iterator` is used then the next character after those copied is read from the stream and then discarded, losing data.

I believe all three of Dinkumware, libc++ and libstdc++ implement `copy_n` with `n - 1` increments of the `InputIterator`, which avoids reading and discarding a character when used with `istream_iterator`, but is inconsistent with `uninitialized_copy_n` and causes surprising behaviour with `istreambuf_iterator` instead, because `copy_n(in, 2, copy_n(in, 2, out))` is not equivalent to `copy_n(in, 4, out)`

[2016-08 Chicago]

Tues PM: refer to LEWG

[LEWG Kona 2017]

This is a mess. Append to Effects: If the InputIterator is not a forward iterator, increments n-1 times. Otherwise the number of increments is not more than n. (ncm) The preceding proposition is unsatisfactory, because it is wrong for istreambuf_iterator, which is much more useful than istream_iterator. Proposing instead: Append to Effects: If InputIterator is istream_iterator for some T, increments n-1 times. Otherwise, increments n times. Want a paper exploring what the implementations actually do, and what non-uniformity is "right".

Status to Open

Proposed resolution:

### 2490(i). `<regex>` needs lots of `noexcept`

Section: 28.6 [re] Status: New Submitter: Stephan T. Lavavej Opened: 2015-03-27 Last modified: 2015-05-05

Priority: 3

View other active issues in [re].

View all other issues in [re].

View all issues with New status.

Discussion:

Only 4 functions are marked `noexcept` in all of Clause 28. Many more need to be marked — for example, `regex_error::code()`, `basic_regex::swap()`, and `sub_match::length()`.

Proposed resolution:

### 2491(i). `std::less<T*>` in constant expression

Section: 22.10.8 [comparisons] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-04-01 Last modified: 2021-04-10

Priority: 3

View other active issues in [comparisons].

View all other issues in [comparisons].

View all issues with New status.

Discussion:

It is not entirely clear if and when the specializations of `std::less` (and friends) for pointer types can be used in a constant expression. Consider the following code:

>     #include <functional>

>     struct foo {};
>     foo x, y;
>     constexpr bool b = std::less<foo*>{}(&x, &y); // [1]

>     foo z[] = {{}, {}};
>     constexpr bool ba = std::less<foo*>{}(&z[0], &z[1]); // [2]

Comparing the address of unrelated objects is not a constant expression since the result is unspecified, so it could be expected for [1] to fail and [2] to succeed. However, `std::less` specialization for pointer types is well-defined and yields a total order, so it could just as well be expected for [1] to succeed. Finally, since the implementation of such specializations is not mandated, [2] could fail as well (This could happen, if an implementation would provide such a specialization and if that would use built-in functions that would not be allowed in constant expressions, for example). In any case, the standard should be clear so as to avoid implementation-defined `constexpr`-ness.

[2017-01-22, Jens provides rationale and proposed wording]

`std::less<T*>` is required to deliver a total order on pointers. However, the layout of global objects is typically determined by the linker, not the compiler, so requiring `std::less<T*>` to provide an ordering at compile-time that is consistent with run-time would need results from linking to feed back to the compiler, something that C++ has traditionally not required.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4618.

> 1. Add at the end of 22.10.8 [comparisons]:

>    > -2- For templates `less`, `greater`, `less_equal`, and `greater_equal`, […], if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by those built-in operators. Relational comparisons of pointer values are not required to be usable as constant expressions.

[2021-04-05; Jiang An comments and provides alternative wording]

The libc++ and MSVC STL implementations only support flat address spaces, and always use comparison operators. The libstdc++ implementation casts pointer values to `uintptr_t` if the direct comparison result is unusable in constant evaluation.

So, I think that we can specify that the implementation-defined strict total order (3.28 [defns.order.ptr]) generates a core constant expression if and only if the corresponding underlying comparison expression comparing pointer values is a core constant expression. No any other case should be a core constant expression, otherwise we should also make the underlying comparison expression a core constant expression.

IMO the proposed resolution is already implemented in libc++, libstdc++, and MSVC STL, and implementable on compilers that either support flat address spaces only or have implemented intrinsics needed for transparent comparison operators and `std::is_constant_evaluated`.

Proposed resolution:

This wording is relative to N4885.

1. Add at the end of 22.10.8 [comparisons] p2:

   > -2- For templates `less`, `greater`, `less_equal`, and `greater_equal`, the specializations for any pointer type yield a result consistent with the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]). [Note 1: If `a < b` is well-defined for pointers `a` and `b` of type `P`, then `(a < b) == less<P>()(a, b)`, `(a > b) == greater<P>()(a, b)`, and so forth. — end note] For template specializations `less<void>`, `greater<void>`, `less_equal<void>`, and `greater_equal<void>`, if the call operator calls a built-in operator comparing pointers, the call operator yields a result consistent with the implementation-defined strict total order over pointers. A comparison result of pointer values is a core constant expression if and only if the corresponding built-in comparison expression is a core constant expression.

2. Add at the end of 22.10.9 [range.cmp] (3.1):

   > -3- Effects:

   > 1. (3.1) — If the expression `std::forward<T>(t) == std::forward<U>(u)` results in a call to a built-in operator `==` comparing pointers: returns `false` if either (the converted value of) `t` precedes `u` or `u` precedes `t` in the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]) and otherwise `true`. The result is a core constant expression if and only if `std::forward<T>(t) == std::forward<U>(u)` is a core constant expression.

   > 2. (3.2) — Otherwise, equivalent to: `return std::forward<T>(t) == std::forward<U>(u);`

3. Add at the end of 22.10.9 [range.cmp] (7.1):

   > -7- Effects:

   > 1. (7.1) — If the expression `std::forward<T>(t) < std::forward<U>(u)` results in a call to a built-in operator `<` comparing pointers: returns `true` if (the converted value of) `t` precedes `u` in the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]) and otherwise `false`. The result is a core constant expression if and only if `std::forward<T>(t) < std::forward<U>(u)` is a core constant expression.

   > 2. (7.2) — Otherwise, equivalent to: `return std::forward<T>(t) < std::forward<U>(u);`

4. Add at the end of 22.10.8.8 [comparisons.three.way] (3.1):

   > -3- Effects:

   > 1. (3.1) — If the expression `std::forward<T>(t) <=> std::forward<U>(u)` results in a call to a built-in operator `<=>` comparing pointers: returns `strong_ordering::less` if (the converted value of) `t` precedes `u` in the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]), `strong_ordering::greater` if `u` precedes `t`, and otherwise `strong_ordering::equal`. The result is a core constant expression if and only if `std::forward<T>(t) <=> std::forward<U>(u)` is a core constant expression.

   > 2. (3.2) — Otherwise, equivalent to: `return std::forward<T>(t) <=> std::forward<U>(u);`

### 2493(i). `initializer_list` supports incomplete classes

Section: 17.11 [support.initlist] Status: New Submitter: David Krauss Opened: 2015-04-27 Last modified: 2015-05-05

Priority: 4

View other active issues in [support.initlist].

View all other issues in [support.initlist].

View all issues with New status.

Discussion:

The typical use-case of `std::initializer_list<T>` is for a pass-by-value parameter of `T`'s constructor. However, this contravenes 16.4.5.8 [res.on.functions]/2.5 because `initializer_list` doesn't specifically allow incomplete types (as do for example `std::unique_ptr` (20.3.1 [unique.ptr]/5) and `std::enable_shared_from_this` (20.3.2.7 [util.smartptr.enab]/2)).

A resolution would be to copy-paste the relevant text from such a paragraph.

Proposed resolution:

### 2496(i). Certain hard-to-avoid errors not in the immediate context are not allowed to be triggered by the evaluation of type traits

Section: 21.3.5.4 [meta.unary.prop] Status: New Submitter: Hubert Tong Opened: 2015-05-07 Last modified: 2015-08-03

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

I do not believe that the wording in 21.3.5.4 [meta.unary.prop] paragraph 3 allows for the following program to be ill-formed:

>     #include <type_traits>

>     template <typename T> struct B : T { };
>     template <typename T> struct A { A& operator=(const B<T>&); };

>     std::is_assignable<A<int>, int> q;

In particular, I do not see where the wording allows for the "compilation of the expression" `declval<T>() = declval<U>()` to occur as a consequence of instantiating `std::is_assignable<T, U>` (where `T` and `U` are, respectively, `A<int>` and `int` in the example code).

Instantiating `A<int>` as a result of requiring it to be a complete type does not trigger the instantiation of `B<int>`; however, the "compilation of the expression" in question does.

Proposed resolution:

### 2497(i). Use of `uncaught_exception()`

Section: 31.7.6.2.4 [ostream.sentry] Status: New Submitter: Roger Orr Opened: 2015-05-08 Last modified: 2020-09-06

Priority: 3

View all other issues in [ostream.sentry].

View all issues with New status.

Discussion:

In the current 31.7.6.2.4 [ostream.sentry], p4 refers to the now deprecated `std::uncaught_exception()`: D.9 [depr.uncaught].

> If `((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.good())` is true, calls `os.rdbuf()->pubsync()`.

This needs to be changed, for example to use `std::uncaught_exceptions()` and to capture the value on entry and compare with the saved value on exit.

[2015-06, Telecon]

JW: I already added an 's' here to make it use the new function, but that doesn't resolve Roger's suggestion to capture the value on entry and check it.

[2019-03-21; Daniel comments and provides wording]

The wording below implements Roger's suggestion.

Proposed resolution:

This wording is relative to N4810.

1. Modify 31.7.6.2.4 [ostream.sentry], class `basic_ostream::sentry` synopsis, as indicated:

   >     namespace std {
   >       template<class charT, class traits = char_traits<charT>>
   >       class basic_ostream<charT, traits>::sentry {
   >         bool ok_; // exposition only
   >         int uncaught_ = uncaught_exceptions(); // exposition only
   >       public:
   >         explicit sentry(basic_ostream<charT, traits>& os);
   >         ~sentry();
   >         explicit operator bool() const { return ok_; }
   >         sentry(const sentry&) = delete;
   >         sentry& operator=(const sentry&) = delete;
   >       };
   >     }

   > […]

   >     ~sentry();

   > > -4- If `(os.flags() & ios_base::unitbuf) && !uncaught_exceptions() <= uncaught_ && os.good()` is `true`, calls `os.rdbuf()->pubsync()`. If that function returns `-1`, sets `badbit` in `os.rdstate()` without propagating an exception.

### 2504(i). `basic_streambuf` is not an abstract class

Section: 31.6.3 [streambuf] Status: New Submitter: Jonathan Wakely Opened: 2015-05-28 Last modified: 2015-08-03

Priority: 3

View all other issues in [streambuf].

View all issues with New status.

Discussion:

31.6.3 [streambuf] p1 says:

> The class template `basic_streambuf<charT, traits>` serves as an abstract base class for deriving various stream buffers whose objects each control two character sequences: […]

The term "abstract base class" is not defined in the standard, but "abstract class" is (11.7.4 [class.abstract]).

According to the synopsis `basic_streambuf` has no pure virtual functions so is not an abstract class and none of libstdc++, libc++, or dinkumware implement it as an abstract class. I don't believe the wording was ever intended to require it to be an abstract class, but it could be read that way.

I suggest the wording be changed to "polymorphic base class" or something else that can't be seen to imply a normative requirement to make it an abstract class.

Proposed resolution:

### 2506(i). Underspecification of atomics

Section: 6.10.2 [intro.multithread], 32.5.8 [atomics.types.generic], 17.14 [support.runtime] Status: SG1 Submitter: Geoffrey Romer Opened: 2015-05-29 Last modified: 2018-03-15

Priority: 3

View all other issues in [intro.multithread].

View all issues with SG1 status.

Discussion:

The concurrency libraries specified in clauses 29 and 30 do not adequately specify how they relate to the concurrency model specified in 6.10.2 [intro.multithread]. In particular:

6.10.2 [intro.multithread] specifies "atomic objects" as having certain properties. I can only assume that instances of the classes defined in Clause 29 are intended to be "atomic objects" in this sense, but I can't find any wording to specify that, and it's genuinely unclear whether Clause 30 objects are atomic objects. In fact, on a literal reading the C++ Standard doesn't appear to provide any portable way to create an atomic object, or even determine whether an object is an atomic object.

(It's not clear if the term "atomic object" is actually needed, given that atomic objects can have non-atomic operations, and non-atomic objects can have atomic operations. But even if the term itself goes away, there still needs to be some indication that Clause 29 objects have the properties currently attributed to atomic objects).

Similarly, 6.10.2 [intro.multithread] uses "atomic operation" as a term of art, but the standard never unambiguously identifies any operation as an "atomic operation" (although in one case it unambiguously identifies an operation that is not atomic). It does come close in a few cases, but not close enough:

- 6.10.2 [intro.multithread]/p7 could be read to imply that "synchronization operations" in Clauses 29 and 30 are also atomic operations. However, that's vague and indirect, and somewhat belied by 32.6.4.2 [thread.mutex.requirements.mutex]/p5, which specifies that mutex lock and unlock operations "behave as atomic operations", but only "for purposes of determining the existence of a data race". Furthermore, not a single operation in Clause 29 explicitly identifies itself as a "synchronization operation".

- 32.5.8 [atomics.types.generic]/p4 states in part that "There shall be a specialization `atomic<bool>` which provides the general atomic operations as specified in 29.6.1", but read in context, "general atomic operations" appears to be a loose synonym for "general operations on atomic types" as defined in [atomics.types.operations.general], rather than a use of "atomic object" as Words of Power. Incidentally, "atomic type" is never satisfactorily defined either (although the `<atomic>` synopsis comes close).

- 17.14 [support.runtime]/p10 specifies exactly which operations are "plain lock-free atomic operations", but in a standard where an "integral constant expression" isn't necessarily a "constant expression", I do not feel safe assuming that a "plain lock-free atomic operation" is an "atomic operation".

- Hans Boehm tells me the operations with "atomically" in the Effects element are intended to be atomic operations, but since "atomic operation" is a term of art (e.g. in 6.10.2 [intro.multithread]/p27.4), I think this needs to be spelled out rather than assumed. Furthermore, this does not help with 32.5.11 [atomics.fences], or anything in Clause 30.

[2018-03 JAX; Geoffrey comments in behalf of SG1]

SG1 consensus is that operations outside clause 32 are not "atomic operations", and objects of types defined outside clause 32 are not "atomic objects". "Synchronization operations" are operations which act as endpoints of primitive edges of partial orders other than sequenced-before, but it may make more sense to just drop that term and inline the definition, so to speak.

We would welcome a paper to make those definitions more explicit, and revise the wording as needed to be consistent with those definitions.

Proposed resolution:

### 2508(i). §[new.delete.dataraces] wording needs to be updated

Section: 17.6.3.5 [new.delete.dataraces] Status: New Submitter: Hans Boehm Opened: 2015-06-09 Last modified: 2016-02-01

Priority: 3

View other active issues in [new.delete.dataraces].

View all other issues in [new.delete.dataraces].

View all issues with New status.

Discussion:

17.6.3.5 [new.delete.dataraces] uses obsolete wording.

It should introduce a "synchronizes with" relationship. "Happens before" is too weak, since that may not composes with sequenced before.

The "shall not introduce a data race" wording is probably not technically correct either. These may race with other (non-allocation/deallocation) concurrent accesses to the object being allocated or deallocated.

Proposed resolution:

### 2512(i). Y2K bites; what is an "unambiguous year identifier"?

Section: 28.3.4.6.2.3 [locale.time.get.virtuals] Status: Open Submitter: Hubert Tong Opened: 2015-06-19 Last modified: 2017-09-07

Priority: 4

View other active issues in [locale.time.get.virtuals].

View all other issues in [locale.time.get.virtuals].

View all issues with Open status.

Discussion:

I recently encountered a failure related to questionable use of `do_get_year`. The platform where the code happened to work had an implementation which handled certain three-digit "year identifiers" as the number of years since 1900 (this article describes such an implementation).

28.3.4.6.2.3 [locale.time.get.virtuals] makes it implementation defined whether two-digit years are accepted, etc., but does not say anything specifically about three-digit years.

The implementation freedom to not report errors in 28.3.4.6.2 [locale.time.get] paragraph 1 also seems to be too broad.

See also the discussion following c++std-lib-38042.

[2016-08 Chicago]

Wed PM: This has been this way since C++98. Don't think it's a P2.

Change to P4, and move to Open.

Proposed resolution:

### 2513(i). Missing requirements for `basic_string::value_type`

Section: 27.1 [strings.general] Status: New Submitter: Jonathan Wakely Opened: 2015-06-26 Last modified: 2020-09-06

Priority: 4

View all other issues in [strings.general].

View all issues with New status.

Discussion:

The allocator-aware container requirements in Table 98 impose no `MoveAssignable` requirements on the `value_type` when `propagate_on_container_move_assignment` is true, because typically the container's storage would be moved by just exchanging some pointers.

However for a `basic_string` using the small string optimization move assignment may need to assign individual characters into the small string buffer, even when the allocator propagates.

The only requirement on the char-like objects stored in a `basic_string` are that they are non-array POD types and `Destructible`, which means that a POD type with a deleted move assignment operator should be usable in a `basic_string`, despite it being impossible to move assign:

>     #include <string>

>     struct odd_pod
>     {
>       odd_pod() = default;
>       odd_pod& operator=(odd_pod&&) = delete;
>     };

>     static_assert(std::is_pod<odd_pod>::value, "POD");

>     int main()
>     {
>       using S = std::basic_string<odd_pod>;
>       S s;
>       s = S{};       // fails
>     }

Using libstdc++ `basic_string<odd_pod>` cannot even be default-constructed because the constructor attempts to assign the null terminator to the first element of the small string buffer.

Similar problems exist with POD types with a deleted default constructor.

I believe that `basic_string` should require its `value_type` to be at least `DefaultConstructible` and `MoveAssignable`.

[2016-06, Oulu]

This should be resolved by P0178

Note: P0178 was sent back to LEWG in Oulu.

Proposed resolution:

### 2528(i). Order of `std::tuple` construction unspecified

Section: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Brian Rodriguez Opened: 2015-08-25 Last modified: 2017-02-19

Priority: 3

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with New status.

Discussion:

The `std::tuple` order of element construction is unspecified. It is either in the same order of the type list or in reverse.

Consider the following program:

>     #include <iostream>
>     #include <tuple>

>     struct X
>     {
>       X(int) { std::cout << "X constructor\n"; }
>     };

>     struct Y
>     {
>       Y(int) { std::cout << "Y constructor\n"; }
>     };

>     int main()
>     {
>       std::tuple<X, Y> t(1, 2);
>     }

Here is a link to two sample compilations. The first uses libstdc++ and constructs in reverse order, and the second uses libc++ and constructs in in-order.

A `std::tuple` mimics both a struct and type-generic container and should thus follow their standards. Construction is fundamentally different from a function call, and it has been historically important for a specific order to be guaranteed; namely: whichever the developer may decide. Mandating construction order will allow developers to reference younger elements later on in the chain as well, much like a struct allows you to do with its members.

There are implementation issues as well. Reversed lists will require unnecessary overhead for braced-initializer-list initialization. Since lists are evaluated from left to right, the initializers must be placed onto the stack to respect the construction order. This issue could be significant for large tuples, deeply nested tuples, or tuples with elements that require many constructor arguments.

I propose that the `std::tuple<A, B, ..., Y, Z>`'s constructor implementation be standardized, and made to construct in the same order as its type list e.g. `A{}, B{}, ..., Y{}, Z{}`.

Daniel:

When N3140 became accepted, wording had been added that gives at least an indication of requiring element initialization in the order of the declaration of the template parameters. This argumentation can be based on 22.4.4.2 [tuple.cnstr] p3 (emphasize mine):

> -3- In the constructor descriptions that follow, let `i` be in the range `[0,sizeof...(Types))` in order, `Ti` be the `ith` type in `Types`, and `Ui` be the `ith` type in a template parameter pack named `UTypes`, where indexing is zero-based.

But the current wording needs to be improved to make that intention clearer and an issue like this one is necessary to be sure that the committee is agreeing (or disagreeing) with that intention, especially because N3140 didn't really point out the relevance of the element construction order in the discussion, and because not all constructors explicitly refer to the ordered sequence of numbers generated by the variable `i` (The move constructor does it right, but most other don't do that).

[2017-02-12, Alisdair comments]

Note that this issue should not be extended to cover the assignment operators, as implementations may want the freedom to re-order member-wise assignment so that, for example, all potentially-throwing assignments are performed before non-throwing assignments (as indicated by the `noexcept` operator).

Proposed resolution:

### 2530(i). Clarify observable side effects of releasing a shared state

Section: 32.10.5 [futures.state] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2016-08-06

Priority: 3

View all other issues in [futures.state].

View all issues with Open status.

Discussion:

When a shared-state is released, it may be necessary to execute user defined code for the destructor of a stored value or exception. It is unclear whether the execution of said destructor constitutes an observable side effect.

While discussing N4445 in Lenexa, Nat Goodspeed pointed out that 32.10.5 [futures.state]/5.1 does not explicitly mention the destruction of the result, so implementations should be allowed to release (or reuse) a shared state ahead of time under the "as-if" rule.

The standard should clarify whether the execution of destructors is a visible side effect of releasing a shared state.

[2016-08-03 Chicago]

This is related to 2532(i)

Fri AM: Moved to Open

Proposed resolution:

### 2532(i). Satisfying a `promise` at thread exit

Section: 32.10.6 [futures.promise] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2016-08-06

Priority: 3

View all other issues in [futures.promise].

View all issues with Open status.

Discussion:

`promise::set_value_at_thread_exit` and `promise::set_exception_at_thread_exit` operate on a shared state at thread exit, without making the thread participate in the ownership of such shared state.

Consider the following snippet:

>     std::promise<int>{}.set_value_at_thread_exit(42);

Arguably, since the `promise` abandons its shared state without actually making it ready, a `broken_promise` error condition should be stored in the shared state. Implementations diverge, they either crash at thread exit by dereferencing an invalid pointer, or keep the shared state around until thread exit.

[2016-08-03 Chicago]

This is related to 2530(i)

[2016-08-03, Billy O'Neal suggests concrete wording]

Fri AM: Moved to Open

Proposed resolution:

This wording is relative to N4606.

1. Change 32.10.5 [futures.state] p7 as indicated:

   > -7- When an asynchronous provider is said to abandon its shared state, it means:

   > 1. (7.1) — first, if that state is not ready or scheduled to be made ready at thread exit, the provider

   >    1. (7.1.1) — stores an exception object of type `future_error` with an error condition of `broken_promise` within its shared state; and then

   >    2. (7.1.2) — makes its shared state ready;

2. Change 32.10.5 [futures.state] p10 as indicated:

   > -10- Some functions (e.g., `promise::set_value_at_thread_exit`) delay making the shared state ready untilschedule the shared state to be made ready when the calling thread exits. This associates a reference to the shared state with the calling thread. The destruction of each of that thread's objects with thread storage duration (6.8.6.3 [basic.stc.thread]) is sequenced before making that shared state ready. When the calling thread makes the shared state ready, if the thread holds the last reference to the shared state, the shared state is destroyed. [Note: This means that the shared state may not become ready until after the asynchronous provider has been destroyed. — end note]

### 2533(i). [concurr.ts] Constrain threads where `future::then` can run a continuation

Section: 99 [concurr.ts::futures.unique.future] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2025-10-20

Priority: 4

View all issues with Open status.

Discussion:

Addresses: concurr.ts

In N4538, the continuation given to `future::then` can be run "on an unspecified thread of execution". This is too broad, as it allows the continuation to be run on the main thread, a UI thread, or any other thread. In comparison, functions given to `async` run "as if in a new thread of execution", while the Parallelism TS gives less guarantees by running "in either the invoking thread or in a thread implicitly created by the library to support parallel algorithm execution". The threads on which the continuation given to `future::then` can run should be similarly constrained.

[2017-03-01, Kona, SG1]

Agreement that this is a problem. Suggested addition to the issue is below. We have no immediate delivery vehicle for a fix at the moment, but we would like to make the intended direction clear.

There is SG1 consensus that `.then` continuations should, by default, and in the absence of executors, be run only in the following ways:

1. If the future is not ready when `.then()` is called, the `.then` argument may be run on the execution agent that fulfills the promise.

2. In all cases, the `.then` argument may be run on an implementation-provided thread, i.e. a thread that is neither the main thread nor explicitly created by the user.

In the absence of an executor argument (which currently cannot be supplied), running of the `.then()` continuation will not block the thread calling `.then()`, even if the future is ready at the time.

Straw polls:

SF | F | N | A | SA

For the default behaviour:

"1. Run on completed task or new execution agent"

0 | 7 | 5 | 1 | 0

"2. Run on completed task or `.then` caller"

0 | 0 | 5 | 5 | 3

"3. Leave as implementation defined"

1 | 2 | 4 | 3 | 3

"4. Always new execution agent"

2 | 3 | 6 | 2 | 0

The actual conclusion was to allow either (1) or (4) for now, since they are quite close, but present a very different programming mode from (2).

[2025-10-20; Reflector poll.]

Set priority to 4 based on age of issue and apparent lack of interest.

Proposed resolution:

### 2546(i). Implementability of locale-sensitive UnicodeEscapeSequence matching

Section: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2015-10-08 Last modified: 2024-10-03

Priority: 4

View other active issues in [re.grammar].

View all other issues in [re.grammar].

View all issues with New status.

Discussion:

In 28.6.12 [re.grammar] paragraph 2:

> `basic_regex` member functions shall not call any locale dependent C or C++ API, including the formatted string input functions. Instead they shall call the appropriate traits member function to achieve the required effect.

Yet, the required interface for a regular expression traits class (28.6.2 [re.req]) does not appear to have any reliable method for determining whether a character as encoded for the locale associated with the traits instance is the same as a character represented by a UnicodeEscapeSequence, e.g., assuming a sane `ru_RU.koi8r` locale:

>     #include <stdio.h>
>     #include <stdlib.h>
>     #include <regex>

>     const char data[] = "\xB3";
>     const char matchCyrillicCaptialLetterYo[] = R"(\u0401)";

>     int main(void)
>     {
>       try {
>         std::regex myRegex;
>         myRegex.imbue(std::locale("ru_RU.koi8r"));

>         myRegex.assign(matchCyrillicCaptialLetterYo, std::regex_constants::ECMAScript);
>         printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str());

>         myRegex.assign("[[:alpha:]]", std::regex_constants::ECMAScript);
>         printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str());
>       } catch (std::regex_error& e) {
>         abort();
>       }
>       return 0;
>     }

The implementation I tried prints:

>     (Ё)
>     (E)

Which means that the character class matching worked, but not the matching to the UnicodeEscapeSequence.

[2024-10-03; Jonathan comments]

`std::basic_regex<charT>` only properly supports matching single code units that fit in `charT`. There's nothing in the spec that supports matching code points that require multiple code units, let alone checking whether a character in an arbitrary encoding corresponds to any given Unicode code point. 28.6.12 [re.grammar] paragraph 12 appears to be an attempt to allow implementations to fail to match here, but is insufficient. When `is_unsigned_v<char>` is true, the CV of the UnicodeEscapeSequence `"\u0080"` is not greater than `CHAR_MAX`, but that doesn't help because U+0080 is encoded as two bytes in UTF-8. Being able to represent `0x80` as `char` does not mean the CV can be matched as a single `char`. The API is unsuitable for Unicode-aware strings.

Proposed resolution:

### 2547(i). Container requirements (and other library text) should say "strict total order", not just "total order"

Section: 22.10.8 [comparisons], 23.2.2 [container.requirements.general], 32.4.3.2 [thread.thread.id] Status: New Submitter: Matt Austern Opened: 2015-10-08 Last modified: 2015-10-21

Priority: 3

View other active issues in [comparisons].

View all other issues in [comparisons].

View all issues with New status.

Discussion:

A number of places in the library, including 22.10.8 [comparisons]/14, the Optional container requirements in 23.2.2 [container.requirements.general], and 32.4.3.2 [thread.thread.id]/8, use the phrase "total order". Unfortunately, that phrase is ambiguous. In mathematics, the most common definition is that a relation `≤` is a total order if it's total, transitive, and antisymmetric in the sense that `x≤y ∧ y≤x ⇒ x=y`. What we really want is a strict total order: a relation `<` is a strict total order if it's total, transitive, and antisymmetric in the sense that exactly one of `x<y`, `y<x`, and `x=y` holds.

The non-normative note in 26.8 [alg.sorting]/4 correctly uses the phrase "strict total ordering" rather than simply "total ordering".

We could address this issue by replacing "total order" with "strict total order" everywhere it appears, since I think there are no cases where we actually want a non-strict total order, or we could add something in Clause 17 saying that we always mean strict total order whenever we say total order.

Proposed resolution:

### 2592(i). Require that `chrono::duration_cast`s from smaller durations to larger durations do not overflow

Section: 30.2 [time.syn] Status: Ready Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2026-06-09

Priority: 4

View all other issues in [time.syn].

View all issues with Ready status.

Discussion:

Currently 30.2 [time.syn] states

>     // convenience typedefs
>     typedef duration<signed integer type of at least 64 bits,        nano> nanoseconds;
>     typedef duration<signed integer type of at least 55 bits,       micro> microseconds;
>     typedef duration<signed integer type of at least 45 bits,       milli> milliseconds;
>     typedef duration<signed integer type of at least 35 bits             > seconds;
>     typedef duration<signed integer type of at least 29 bits, ratio<  60>> minutes;
>     typedef duration<signed integer type of at least 23 bits, ratio<3600>> hours;

However, a `duration_cast<minutes>(seconds::max())` would cause overflow if the underlying signed integers only met the minimums specified.

The standard should specify that implementations guarantee that a `duration_cast` from any smaller duration in these "convenience typedefs" will not overflow any larger duration. That is, `hours` should be able to hold the maximum of `minutes`, which should be able to hold the maximum of `seconds` and so on.

More formally, if the ratio `typedef A` and `typedef B` is `1:Y` where `Y > 1` (e.g., 1 : 60 in case of `minutes` : `seconds`), then `#bitsA-1` must be at least `ceil(log2(2#bitsB-1)/Y))`.

In the case of `minutes` : `seconds`, `X = 1`, `Y = 60`. Let `#bitsseconds = 32`. Therefore:

- `2(#bitsseconds - 1) = 231 = 2147483648`

- `ceil(log2(231 / 60) = 26`

- `#bitsminutes - 1 = 26`

- `#bitsminutes = 27`

Therefore, a minimum of 27 bits would be needed to store `minutes` if 32 were used to store `seconds`.

I propose to change the definitions of the convenience typedefs as follows:

>     // convenience typedefs
>     typedef duration<signed integer type of at least 64 bits,        nano> nanoseconds;
>     typedef duration<signed integer type of at least 55 bits,       micro> microseconds;
>     typedef duration<signed integer type of at least 46 bits,       milli> milliseconds;
>     typedef duration<signed integer type of at least 37 bits             > seconds;
>     typedef duration<signed integer type of at least 32 bits, ratio<  60>> minutes;
>     typedef duration<signed integer type of at least 27 bits, ratio<3600>> hours;

These bits were chosen to satisfy the above formula. Note that minimums only increased, so larger ranges could be held. A nice outcome of this choice is that `minutes` does not go above 32 bits.

[2016-04-23, Tim Song comments]

The P/R of LWG 2592 doesn't fix the issue it wants to solve, because the actual underlying type will likely have more bits than the specified minimum.

Consider `seconds`, which the P/R requires to have at least 37 bits. On a typical system this implies using a 64-bit integer. To ensure that casting from `seconds::max()` to `minutes` doesn't overflow in such a system, it is necessary for the latter to have at least 59 bits (which means, in practice, 64 bits too), not just 32 bits. Thus, just changing the minimum number of bits will not be able to provide the desired guarantee that casting from a smaller unit to a larger one never overflow.

If such a guarantee is to be provided, it needs to be spelled out directly. Note that the difference here is 9 bits (for the 1000-fold case) and 5 bits (for the 60-fold case), which is less than the size difference between integer types on common systems, so such a requirement would effectively require those convenience typedefs to use the same underlying integer type.

[Brno 2026-06-09; Change status New → Ready.]

Proposed resolution:

This wording is relative to N4567.

1. Change 30.2 [time.syn], header `<chrono>` synopsis, as indicated

   >     […]

   >     // convenience typedefs
   >     typedef duration<signed integer type of at least 64 bits,        nano> nanoseconds;
   >     typedef duration<signed integer type of at least 55 bits,       micro> microseconds;
   >     typedef duration<signed integer type of at least 4645 bits,       milli> milliseconds;
   >     typedef duration<signed integer type of at least 3735 bits             > seconds;
   >     typedef duration<signed integer type of at least 3229 bits, ratio<  60>> minutes;
   >     typedef duration<signed integer type of at least 2723 bits, ratio<3600>> hours;

   >     […]

### 2594(i). Contradicting definition of empty `shared_ptr` on `shared_ptr(nullptr, d)`

Section: 20.3.2.2 [util.smartptr.shared] Status: New Submitter: Kazutoshi Satoda Opened: 2016-02-20 Last modified: 2016-06-20

Priority: 3

View all other issues in [util.smartptr.shared].

View all issues with New status.

Discussion:

Latest draft (N4567) 20.3.2.2 [util.smartptr.shared] p1 says:

> A `shared_ptr` object is empty if it does not own a pointer.

Please note that it says "own a pointer". This definition was added as the resolution for LWG defect 813(i).

20.3.2.2.2 [util.smartptr.shared.const] p8 says about the effect of `shared_ptr(nullptr_t p, D d)`:

> Effects: Constructs a `shared_ptr` object that owns the object `p` and the deleter `d`.

Please note that it says "owns the object". This was intentionally changed from "the pointer" as a part of resolution for LWG defect 758(i), to cover `nullptr_t` case.

Since `shared_ptr(nullptr, d)` owns an object of type `nullptr_t`, but does not own a pointer, it is said as "empty" by a strict reading of the above mentioned definition in 20.3.2.2 [util.smartptr.shared] p1.

These cause a contradiction:

20.3.2.2.2 [util.smartptr.shared.const] p9 sets a postcondition `use_count() == 1` on `shared_ptr(nullptr, d)`. But 20.3.2.2.6 [util.smartptr.shared.obs] p7 says that the return value of `use_count()` is "`0` when `*this` is empty".

Proposed wording changes:

Replace the last 2 words in 20.3.2.2 [util.smartptr.shared] p1 from

> […] empty if it does not own a pointer.

to

> […] empty if it does not own an object.

Note that `shared_ptr(nullptr_t)` is defined to be empty in synopsis in 20.3.2.2 [util.smartptr.shared].

>     constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }

It could be less confusing if `shared_ptr(nullptr, d)` could be defined to be empty. But it seems too late to change that (which means changing whether the deleter is called or not, see this Stackoverflow article). Then I'm proposing just fix the contradiction.

Proposed resolution:

This wording is relative to N4594.

1. Change 20.3.2.2 [util.smartptr.shared] p1 as indicated:

   > -1- The `shared_ptr` class template stores a pointer, usually obtained via `new`. `shared_ptr` implements semantics of shared ownership; the last remaining owner of the pointer is responsible for destroying the object, or otherwise releasing the resources associated with the stored pointer. A `shared_ptr` object is empty if it does not own an objecta pointer.

### 2595(i). `reverse_iterator::operator[]`'s return type revisited

Section: 24.5.1.2 [reverse.iterator], 24.5.1.6 [reverse.iter.elem] Status: Ready Submitter: Robert Haberlach Opened: 2016-02-28 Last modified: 2026-06-09

Priority: 3

View all other issues in [reverse.iterator].

View all issues with Ready status.

Discussion:

Issue 386(i) changed the return type of `reverse_iterator::operator[]` to unspecified. However, as of N3066, the return type of a random access iterator's `operator[]` shall be convertible to `reference`; thus the return type of `reverse_iterator::operator[]` should be `reference` (and it is in all common implementations).

Suggested resolution: Adjust 24.5.1.2 [reverse.iterator]'s synopsis and 24.5.1.6 [reverse.iter.elem] to use `reference` instead of `unspecified`.

[2021-06-06 Tim syncs wording to current working draft]

[Brno 2026-06-09; Change status New → Ready.]

Proposed resolution:

This wording is relative to N4885.

1. Edit 24.5.1.2 [reverse.iterator], class template `` synopsis, as indicated:

   >     namespace std {
   >       template <class Iterator>
   >       class reverse_iterator {
   >       public:
   >         […]
   >         using reference         = iter_reference_t<Iterator>;
   >         […]
   >         constexpr referenceunspecified operator[](difference_type n) const;
   >         […]
   >       };
   >     }

2. Change 24.5.1.6 [reverse.iter.elem] before p3 as indicated:

   >     constexpr referenceunspecified operator[](difference_type n) const;

### 2599(i). Library incomplete type permission phrase is unclear

Section: 22.2.6 [declval], 20.3.1 [unique.ptr], 20.3.1.2.1 [unique.ptr.dltr.general], 20.3.2.2 [util.smartptr.shared], 20.3.2.3 [util.smartptr.weak], 20.3.2.7 [util.smartptr.enab] Status: New Submitter: Zhihao Yuan Opened: 2016-03-08 Last modified: 2016-04-16

Priority: 3

View other active issues in [declval].

View all other issues in [declval].

View all issues with New status.

Discussion:

Currently the phrase to grant this permission is:

> The template parameter `T` of `LibraryTemplate` may be an incomplete type.

Two problems:

1. The timing is unclear. We always allow specializations like `LibraryTemplate<Incomp>* p;`

2. To the users of a template, the correct terminology should be "argument" rather than "parameter".

Suggested resolution:

> In an instantiation of `LibraryTemplate`, an incomplete type may be used as the template argument for the template parameter `T`.

as shown here.

Or, to copy N4510's wording:

> An incomplete type `T` may be used when instantiating `LibraryTemplate`.

Proposed resolution:

### 2675(i). `register_callback` can fail

Section: 31.5.2.7 [ios.base.callback] Status: New Submitter: David Krauss Opened: 2016-03-14 Last modified: 2019-06-15

Priority: 3

View all issues with New status.

Discussion:

`register_callback` allocates memory and so it can fail, but the case is unspecified. libc++ sets `badbit`, which is consistent with `iword` and `pword`. libstdc++ throws `std::bad_alloc`.

[2019-06-13; Billy comments]

Just as an additional data point: MSVC++ agrees with libstdc++ and also throws `std::bad_alloc`.

Proposed resolution:

### 2691(i). `money_base::space` and `do_put`: U+0020 versus `fill`

Section: 28.3.4.7.4 [locale.moneypunct] Status: New Submitter: Hubert Tong Opened: 2016-04-12 Last modified: 2016-05-22

Priority: 3

View all other issues in [locale.moneypunct].

View all issues with New status.

Discussion:

The description of `money_base::space` is that "at least one space is required at that position." (N4582 subclause 22.4.6.3 [locale.moneypunct] paragraph 2)

When formatting for output (28.3.4.7.3.3 [locale.money.put.virtuals]), it is not clear that

1. "the number of characters generated for the specified format" (excluding fill padding) includes exactly one character for `money_base::space` (if present), and

2. all characters corresponding to `money_base::space` (excluding fill padding) are copies of `fill`.

In particular, there is implementation divergence over point (b) as to whether U+0020 or `fill` should be used. Further, should a character other than `fill` be used, it is unclear when "the fill characters are placed where `none` or `space` appears in the formatting pattern", whether the fill characters are placed at the beginning or the end of the "space field".

I believe that a strict interpretation of the current wording supports U+0020; however, `fill` is more likely to be the pragmatic choice.

Proposed resolution:

This wording is relative to N4582.

1. Change 28.3.4.7.4 [locale.moneypunct] paragraph 2 as indicated:

   > -2- Where `none` or `space` appears, white space is permitted in the format, except where `none` appears at the end, in which case no white space is permitted. For input, the value `space` indicates that at least one space is required at that position. For output, the value `space` indicates one instance of the fill character (28.3.4.7.3.3 [locale.money.put.virtuals]).The value `space` indicates that at least one space is required at that position. Where `symbol` appears, the sequence of characters returned by `curr_symbol()` is permitted, and can be required. Where `sign` appears, the first (if any) of the sequence of characters returned by `positive_sign()` or `negative_sign()` (respectively as the monetary value is non-negative or negative) is required. Any remaining characters of the sign sequence are required after all other format components. Where `value` appears, the absolute numeric monetary value is required.

### 2695(i). "As if" unclear in [member.functions]

Section: 16.4.6.5 [member.functions] Status: New Submitter: Hubert Tong Opened: 2016-04-15 Last modified: 2017-02-02

Priority: 3

View other active issues in [member.functions].

View all other issues in [member.functions].

View all issues with New status.

Discussion:

In N4582 subclause 17.6.5.5 [member.functions], the requirement that:

> any call to the member function that would select an overload from the set of declarations described in this standard behaves as if that overload were selected

is unclear in the extent of the "as if". For example, in providing:

>     basic_string(const charT* s);

for a one-argument call to:

>     basic_string(const charT* s, const Allocator& a = Allocator());

it can be read that an implementation may be required to call the copy constructor for the allocator since the core language rules for copy elision would not allow the "a" argument to be constructed directly into the member used to store the allocator.

Clarification (even if just a note) would be appreciated.

[2016-05 Issues Telecon]

This is related to issue 2563(i).

Proposed resolution:

### 2702(i). `num_put::do_put(..., bool)` performs ill-formed `do_put` call

Section: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2016-05-22

Priority: 3

View other active issues in [facet.num.put.virtuals].

View all other issues in [facet.num.put.virtuals].

View all issues with New status.

Discussion:

The call to `do_put(out, str, fill, (int)val)` in N4582 subclause 28.3.4.3.3.3 [facet.num.put.virtuals] paragraph 6 cannot select a best viable function in overload resolution given the overloads listed for `do_put` in 28.3.4.3.3 [locale.nm.put].

There is implementation divergence:

- Some implementations call the `long` overload (as overriden);

- some implementations call the `unsigned long` overload (as overriden);

- some implementations call something else.

It appears that the resolution to DR 359(i) attempted a fix; however, the relevant portion of the change was not applied to the WP.

Proposed resolution:

### 2703(i). No provision for fill-padding when `boolalpha` is set

Section: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2018-02-11

Priority: 3

View other active issues in [facet.num.put.virtuals].

View all other issues in [facet.num.put.virtuals].

View all issues with New status.

Discussion:

N4582 subclause 28.3.4.3.3.3 [facet.num.put.virtuals] paragraph 6 makes no provision for fill-padding in its specification of the behaviour when `(str.flags() & ios_base::boolalpha) != 0`.

[2017-07-06, Marshall comments]

All the other cases from `num_put` — `int`, `long`, etc all are covered in 28.3.4.3.3.3 [facet.num.put.virtuals] p1 .. p5, which describe how to align and pad the output. (Specifically, stage 3) p6 does not.

With this description:

>     cout << std::setw(15) << false;

outputs:

                  0
    ﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎// Column counter

but

>     cout << std::setw(15) << boolalpha << false;

outputs:

    false

libc++ implements this exactly.
Dinkumware, libstdc++ and MSVC apply padding and alignment.

I think that applying padding and alignment is more appropriate.

Proposed resolution:

### 2705(i). Questionable precondition on Sequence containers `a.assign(n, t)`

Section: 23.2.4 [sequence.reqmts] Status: New Submitter: Kazutoshi Satoda Opened: 2016-05-08 Last modified: 2020-09-06

Priority: 3

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with New status.

Discussion:

Please look through the following modifications on a value `v` of type `vector<T>`:

>     assert(v.size() > 0);
>     v.push_back(v[0]);
>     v.insert(v.begin(), v[0]);
>     v.resize(v.size() * 2, v[0]);
>     v.assign(v.size() * 2, v[0]);

All of these use an element of itself which may be moved or destroyed by the modification.

From what I see so far, the first three are required to work. Please see library issue 526(i) for validity of them.

But only the last one is undefined because it violates a precondition of a sequence container operation. I think this is too subtle.

Should it be like that, really?

The precondition is in Table 107 "Sequence container requirements" at the next of 23.2.4 [sequence.reqmts] p3.

> In Tables 107 and 108, `X` denotes a sequence container class, `a` denotes a value of `X` containing elements of type `T`, […] `n` denotes a value of `X::size_type`, […] `t` denotes an lvalue or a `const` rvalue of `X::value_type`, […]

> […]

> Table 107 — Sequence container requirements (in addition to container)
> Expression | Return type | Assertion/note pre-/post-condition
> --- | --- | ---
> `[…]`
> `a.assign(n, t)` | `void` | Requires: `T` shall be `CopyInsertable` into `X` and `CopyAssignable`. pre: `t` is not a reference into `a`. Replaces elements in `a` with `n` copies of `t`.

I looked into the following implementations:

- libc++ relies on the precondition.

  It deallocates first on `n > capacity()` case, see here.

- libstdc++ doesn't rely on the precondition.

  It creates temporary `vector(n, t)` and `swap()` on `n > capacity()` case, see here.

- MSVC relies on the precondition.

  It unconditionally does `clear()` and then `insert(begin(), n, t)`. I looked into my local "`%PROGRAMFILES(X86)%/Microsoft Visual Studio 14.0/VC/include/vector`".

One drawback of libstdc++ implementation, I could find so far, is possibly increased peek memory usage (both old and new buffer exist at the same time). But, because the same can happen on the most other modifications, it seems a reasonable trade-off to remove the precondition to fill the subtle gap. Users who really needs less memory usage can do `clear()` and `insert()` by themselves.

I also found that `basic_string::assign(n, c)` is safe on this point. At 27.4.3.7.3 [string.assign] p17:

>     basic_string& assign(size_type n, charT c);

> > Effects: Equivalent to `assign(basic_string(n, c))`.

> > Returns: `*this`.

This can be seen as another gap.

Looking back on the history, I found that the definition of `assign(n, t)` was changed at C++14 for library issue 2209(i). There were more restricting definitions like this:

>     void assign(size_type n, const T& t);

> > Effects:

> > >     erase(begin(), end());
> > >     insert(begin(), n, t);

I think the precondition was probably set to accept this old definition and is not required inherently. And if the less memory usage was really intended, the standard is now underspecifying about that.

[2016-05 Issues Telecon]

Howard believes this should be NAD, but we tabled the discussion.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4582.

> 1. In 23.2.4 [sequence.reqmts], edit Table 107 (Sequence container requirements) as indicated:

>    > Table 107 — Sequence container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition
>    > --- | --- | ---
>    > `[…]`
>    > `a.assign(n, t)` | `void` | Requires: `T` shall be `CopyInsertable` into `X` and `CopyAssignable`. pre: `t` is not a reference into `a`. Replaces elements in `a` with `n` copies of `t`.

[2020-04-25, Daniel syncs current wording with recent working draft]

Proposed resolution:

This wording is relative to N4861.

1. In 23.2.4 [sequence.reqmts], edit Table [tab:container.seq.req] (Sequence container requirements) as indicated:

   > Table 77 — Sequence container requirements (in addition to container) [tab:container.seq.req]
   > Expression | Return type | Assertion/note pre-/post-condition
   > --- | --- | ---
   > `[…]`
   > `a.assign(n, t)` | `void` | Preconditions: `T` is Cpp17CopyInsertable into `X` and Cpp17CopyAssignable. `t` is not a reference into `a`. Effects: Replaces elements in `a` with `n` copies of `t`. Invalidates all references, pointers and iterators referring to the elements of `a`. For `vector` and `deque`, also invalidates the past-the-end iterator.

### 2708(i). `recursive_directory_iterator::recursion_pending()` is incorrectly specified

Section: 31.12.12.2 [fs.rec.dir.itr.members] Status: Open Submitter: Eric Fiselier Opened: 2016-05-09 Last modified: 2022-12-18

Priority: 2

View all other issues in [fs.rec.dir.itr.members].

View all issues with Open status.

Discussion:

The current specification of `recursion_pending()` says (31.12.12.2 [fs.rec.dir.itr.members]/24):

> Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false`.

This language does not take into account cases where the prior construction was a copy construction from a iterator, `it`, where `it.recursion_pending() == false`.

[2016-08 Chicago]

Wed AM: Move to Open

[2018-1-26 issues processing telecon]

Status to 'Tentatively Ready'; Casey will explore whether making `recursion_pending` an exposition-only member makes this clearer.

Previous resolution from Eric [SUPERSEDED]:

> This wording is relative to N4582.

> 1. Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:

>    >     explicit recursive_directory_iterator(const path& p);
>    >     recursive_directory_iterator(const path& p, directory_options options);
>    >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>    >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

>    > > […]

>    > > -3- Postcondition: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `recursion_pending() == true`.

>    > > […]

>    > > [Drafting note: The following changes the specification of `recursion_pending()` seemingly recursive. Perhaps it would be easier to specify `recursion_pending()` in terms of a exposition only member in `recursive_directory_iterator`.]

>    >     bool recursion_pending() const;

>    > > […]

>    > > -24- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false` `false` if `disable_recursion_pending()` has been called subsequent to the prior construction or increment operation, otherwise the value of `recursion_pending()` set by that operation.

>    > > […]

>    >     recursive_directory_iterator& operator++();
>    >     recursive_directory_iterator& increment(error_code& ec) noexcept;

>    > > […]

>    > > -27- Effects: As specified by Input iterators (24.2.3), except that: […]

>    > > -?- Postcondition: `recursion_pending() == true`.

[2018-01-29: Casey provides a PR with an exposition-only member]

Status to 'Review'.

Previous resolution from Casey [SUPERSEDED]:

> This wording is relative to N4713.

> 1. Change [fs.rec.dir.itr] as indicated:

>    >         […]

>    >         // other members as required by 24.3.5.3 [input.iterators], input iterators

>    >       private:
>    >         bool recurse_; // exposition-only
>    >       };
>    >     }

> 2. Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:

>    >     explicit recursive_directory_iterator(const path& p);
>    >     recursive_directory_iterator(const path& p, directory_options options);
>    >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>    >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

>    > > […]

>    > > -3- Postconditions: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `recurse_ == true`.

>    > > […]

>    >     recursive_directory_iterator(const recursive_directory_iterator& rhs);

>    > > […]

>    > > -8- Postconditions:

>    > > […]

>    > > (8.3) — `recursion_pending() == rhs.recursion_pending()``recurse_ == rhs.recurse_`

>    >     recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;

>    > > […]

>    > > -10- Postconditions: `options()`, `depth()`, and `recursion_pending()``recurse_` have the values that `rhs.options()`, `rhs.depth()`, and `rhs.recursion_pending()``rhs.recurse_`, respectively, had before the function call.

>    >     recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);

>    > > […]

>    > > -12- Postconditions:

>    > > […]

>    > > (12.3) — `recursion_pending() == rhs.recursion_pending()``recurse_ == rhs.recurse_`

>    > > […]

>    >     recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;

>    > > […]

>    > > -15- Postconditions: `options()`, `depth()`, and `recursion_pending()``recurse_` have the values that `rhs.options()`, `rhs.depth()`, and `rhs.recursion_pending()``rhs.recurse_`, respectively, had before the function call.

>    > > […]

>    >     bool recursion_pending() const;

>    > > -21- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false` recurse_.

>    > > […]

>    >     recursive_directory_iterator& operator++();
>    >     recursive_directory_iterator& increment(error_code& ec) noexcept;

>    > > -23- Effects: As specified for the prefix increment operation of Input iterators ( [iterators.input]), except that:

>    > > […]

>    > > -?- Postcondition: `recurse_ == true`.

>    >     void disable_recursion_pending();

>    > > -28- Postconditions: `recursion_pending()recurse_ == false`.

>    > > […]

[2018-05-23: Casey restores the intended design with an expansion of the original PR]

The intended design is that all copies of a single `recursive_directory_iterator` share a common block of state which includes the values returned by `options`, `depth`, and `recursion_pending` - hence the mandate that those functions not be called on a non-dereferenceable iterator in 31.12.12 [fs.class.rec.dir.itr] para 2. To allow an implementation with such shared state, it's necessary to make changes to the value returned by `recursion_pending()` visible to all copies of the same dereferenceable iterator.

Also:

- `pop` notionally calls `increment` repeatedly until the current directory is exhausted, `pop` should affect the value of `recursion_pending` similarly to `increment`.

- `options` is not valid for all constructor signatures described by 31.12.12.2 [fs.rec.dir.itr.members] para 2.

- the copies and moves don't specify what they actually do

- it's not quite kosher for the copies and moves to have postconditions on the value of expressions that have UB if the iterator copied/moved from is not dereferenceable.

[2018-06, Rapperswil, Wednesday evening]

JW: p21 currently can just say "unspecified"
BO: if we are OK with only remote implementations we can remove the unspecifiedness
BO: the problematic business is the "recursion pending" bit
JW: I want time to work on this

Move to open and note that Jonathan is reviewing and making recommendations.

[2018-08-23 Batavia Issues processing]

General agreement that flag should be shared; Casey to reword.

[2022-12-18; Daniel comments]

Note that this proposed wording has some overlap with LWG 3668(i) for `recursive_directory_iterator`'s constructors without `options` argument. If we would like a different wording form for this textual location in one issue we should resync the other issue to reduce the chance of a merge conflict.

Proposed resolution:

This wording is relative to N4750.

1. Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:

   >     explicit recursive_directory_iterator(const path& p);
   >     recursive_directory_iterator(const path& p, directory_options options);
   >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
   >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

   > > -?- For the signatures with no parameter `options`, let `options` be `directory_options::none`.

   > > -2- Effects: […]

   > > -3- Postconditions: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

   > > - `this->options() == options`

   > > - `recursion_pending() == true`

   > > […]

   >     recursive_directory_iterator(const recursive_directory_iterator& rhs);

   > > -7- Effects: Constructs an object of class `recursive_directory_iterator` iterator that denotes the same directory entry as `rhs`, if any..

   > > -8- Postconditions: If `rhs` is dereferenceable,

   > > […]

   >     recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;

   > > -9- Effects: Constructs an object of class `recursive_directory_iterator` iterator that denotes the directory entry denoted by `rhs` before the function call, if any..

   > > -10- Postconditions: If `rhs` is dereferenceable, […]

   >     recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);

   > > -11- Effects: If `*this` and `rhs` are the same object, the member has no effect. Causes `*this` to denote the same directory entry denoted by `rhs`, if any.

   > > -12- Postconditions: If `rhs` is dereferenceable,

   > > […]

   >     recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;

   > > -14- Effects: If `*this` and `rhs` are the same object, the member has no effect. Causes `*this` to denote the directory entry denoted by `rhs` before the function call, if any.

   > > -15- Postconditions: If `rhs` was dereferenceable before the function call, […]

   > > -16- Returns: `*this`.

   > > -x- Remarks: If `*this` and `rhs` do not refer to the same object, the resulting state of `rhs` is unspecified (16.4.6.17 [lib.types.movedfrom]).

   >     directory_options options() const;

   > > -17- Returns: The value of the argument passed to the constructor for the `options` parameter, if present, otherwise `directory_options::none` established by the most recently called member that has a postcondition for `options()`.

   > > […]

   >     bool recursion_pending() const;

   > > -21- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false`. If `disable_recursion_pending()` has been called on a copy of `*this`, an unspecified value. Otherwise, the value established for `recursion_pending()` by the postcondition of the most recent construction, assignment, increment, or `disable_recursion_pending` operation.

   > > […]

   >     recursive_directory_iterator& operator++();
   >     recursive_directory_iterator& increment(error_code& ec);

   > > -23- Effects: As specified for the prefix increment operation of Input iterators (24.3.5.3 [input.iterators]), except that: […]

   > > -?- Postconditions: If `*this` is dereferenceable, `recursion_pending() == true`.

   > > […]

   >     void pop();
   >     void pop(error_code& ec);

   > > -26- Effects: If `depth() == 0`, set `*this` to `recursive_directory_iterator()`. […]

   > > -?- Postconditions: If `*this` is dereferenceable, `recursion_pending() == true`.

   > > […]

### 2713(i). More missing allocator-extended constructors for unordered containers

Section: 23.5 [unord] Status: Ready Submitter: Billy Robert O'Neal III Opened: 2016-05-20 Last modified: 2026-06-12

Priority: 3

View all other issues in [unord].

View all issues with Ready status.

Discussion:

The resolution of LWG 2210(i) missed constructors accepting a range or initializer list and allocator.

Previous resolution [SUPERSEDED]

This wording is relative to N4582.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_map {
   >       public:
   >         […]
   >         unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >           : unordered_map(f, l, see below, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >           : unordered_map(il, see below, hasher(), key_equal(), a) { }
   >         unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >           : unordered_multimap(f, l, see below, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >           : unordered_multimap(il, see below, hasher(), key_equal(), a) { }
   >         unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

3. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_set {
   >       public:
   >         […]
   >         unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >           : unordered_set(f, l, see below, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >           : unordered_set(il, see below, hasher(), key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >           : unordered_multiset(f, l, see below, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >           : unordered_multiset(il, see below, hasher(), key_equal(), a) { }
   >         unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

[2016-06, Oulu — Daniel comments and provides new wording]

During the LWG discussion of this issue it has been observed, that the interpretation of the embedded see below is not really clear and that we should split declaration and definition of the new overloads, so that we have a place that allows us to specify what "see below" stands for. In addition, the new wording wraps the "see below" as "`size_type(see below)`" to clarify the provided expression type, similar as we did for the default constructor of `unordered_map`.

[Oulu, 2016-06]

Alisdair to review wording.

Previous resolution [SUPERSEDED]

This wording is relative to N4594.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_map {
   >       public:
   >         […]
   >         unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         unordered_map(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2

   >     template <class InputIterator>
   >       unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The number of buckets is implementation-defined.

3. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2

   >     template <class InputIterator>
   >       unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The number of buckets is implementation-defined.

5. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_set {
   >       public:
   >         […]
   >         unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

6. Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2

   >     template <class InputIterator>
   >       unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The number of buckets is implementation-defined.

7. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

8. Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2

   >     template <class InputIterator>
   >       unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The number of buckets is implementation-defined.

[2017-08-04, Daniel and Alisdair finetune wording]

We decided to improve the added Remarks: elements by changing from the previous form:

> Remarks: The number of buckets is implementation-defined.

to the more elaborate form:

> Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

[2020-11-29; Reflector discussions]

It has been pointed out that this issue is related to LWG 1199(i), LWG 2210(i), and LWG 3506(i).

Previous resolution [SUPERSEDED]

This resolution is relative to N4687.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_map {
   >       public:
   >         […]
   >         unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         unordered_map(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2

   >     template <class InputIterator>
   >       unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

3. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template <class Key, class T,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<std::pair<const Key, T> > > {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2

   >     template <class InputIterator>
   >       unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

5. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_set {
   >       public:
   >         […]
   >         unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

6. Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2

   >     template <class InputIterator>
   >       unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

7. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template <class Key,
   >         class Hash = hash<Key>,
   >         class Pred = std::equal_to<Key>,
   >         class Allocator = std::allocator<Key> > {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template <class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

8. Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2

   >     template <class InputIterator>
   >       unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

[2022-07-10; Daniel comments]

It is has been noticed by Daniel Eiband on [std-discussion] that the following deduction guides for the following constructors of the set types `std::unordered_set` and `std::unordered_multiset` are missing:

>     unordered_set(InputIterator, InputIterator, Allocator);
>     unordered_set(initializer_list<T>, Allocator);

>     unordered_multiset(InputIterator, InputIterator, Allocator);
>     unordered_multiset(initializer_list<T>, Allocator);

Since this issue is adding these missing constructors it should also add the associated deduction guides. The proposed wording has been updated to this effect and also rebased to N4910.

Previous resolution [SUPERSEDED]

This resolution is relative to N4910.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_map {
   >       public:
   >         […]
   >         unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         […]
   >         unordered_map(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2

   >     template<class InputIterator>
   >       unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

3. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         […]
   >         unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2

   >     template<class InputIterator>
   >       unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

5. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_set {
   >       public:
   >         […]
   >         unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
   >           -> unordered_set<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_set(InputIterator, InputIterator, Allocator)
   >           -> unordered_set<iter-value-type<InputIterator>,
   >                            hash<iter-value-type<InputIterator>>,
   >                            equal_to<iter-value-type<InputIterator>>,
   >                            Allocator>;

   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;
   >     }

6. Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2

   >     template<class InputIterator>
   >       unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

7. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Hash, Allocator)
   >           -> unordered_multiset<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_multiset(InputIterator, InputIterator, Allocator)
   >           -> unordered_multiset<iter-value-type<InputIterator>,
   >                                 hash<iter-value-type<InputIterator>>,
   >                                 equal_to<iter-value-type<InputIterator>>,
   >                                 Allocator>;

   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
   >     }

8. Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2

   >     template<class InputIterator>
   >       unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

[2022-07-15; Casey comments]

P1206R7 added `from_range_t` constructors corresponding to existing iterator pair constructors for the standard containers. For consistency, this issue should add `from_range_t` constructors corresponding to each new iterator pair constructor.

[2022-07-16; Daniel comments and updates wording]

The new `from_range_t` constructors have been added for each added new iterator pair constructor. Note that the corresponding deduction guides already exist.

Previous resolution [SUPERSEDED]

This resolution is relative to N4910.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_map {
   >       public:
   >         […]
   >         unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >             : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >             : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           unordered_map(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
   >             : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }
   >         unordered_map(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2

   >     template<class InputIterator>
   >       unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       unordered_map(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_map(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

3. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >             : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >             : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           unordered_multimap(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_multimap(from_range, std::forward<R>(rg),
   >                                  n, hasher(), key_equal(), a) { }
   >         […]
   >         unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2

   >     template<class InputIterator>
   >       unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       unordered_multimap(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_multimap(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

5. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_set {
   >       public:
   >         […]
   >         unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >             : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >             : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         unordered_set(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           unordered_set(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
   >           -> unordered_set<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_set(InputIterator, InputIterator, Allocator)
   >           -> unordered_set<iter-value-type<InputIterator>,
   >                            hash<iter-value-type<InputIterator>>,
   >                            equal_to<iter-value-type<InputIterator>>,
   >                            Allocator>;

   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;
   >     }

6. Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2

   >     template<class InputIterator>
   >       unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       unordered_set(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_set(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

7. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                 const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           unordered_multiset(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_multiset(from_range, std::forward<R>(rg),
   >                                  n, hasher(), key_equal(), a) { }
   >         […]
   >         unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
   >         unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Hash, Allocator)
   >           -> unordered_multiset<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_multiset(InputIterator, InputIterator, Allocator)
   >           -> unordered_multiset<iter-value-type<InputIterator>,
   >                                 hash<iter-value-type<InputIterator>>,
   >                                 equal_to<iter-value-type<InputIterator>>,
   >                                 Allocator>;

   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
   >     }

8. Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2

   >     template<class InputIterator>
   >       unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       unordered_multiset(from_range_t, R&& rg, const allocator_type& a))
   >         : unordered_multiset(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

[Brno 2026-06-11; Jonathan provides new wording]

Needs `constexpr` sprinkled everywhere, rebase on current working draft.

[Brno 2026-06-11; Change status: New → Ready.]

Proposed resolution:

This resolution is relative to N5046.

1. Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_map {
   >       public:
   >         […]
   >         constexpr unordered_map(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_map(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           constexpr unordered_map(InputIterator f, InputIterator l, size_type n,
   >                                   const allocator_type& a)
   >             : unordered_map(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                                   const allocator_type& a)
   >             : unordered_map(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_map(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf,
   >                                   const allocator_type& a)
   >             : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }
   >         constexpr unordered_map(initializer_list<value_type> il, const allocator_type& a);
   >         constexpr unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_map(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

2. Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2

   >     template<class InputIterator>
   >       constexpr unordered_map(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       constexpr unordered_map(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_map(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     constexpr unordered_map(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

3. Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class T,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<pair<const Key, T>>> {
   >       class unordered_multimap {
   >       public:
   >         […]
   >         constexpr unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multimap(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           constexpr unordered_multimap(InputIterator f, InputIterator l, size_type n,
   >                                        const allocator_type& a)
   >             : unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_multimap(InputIterator f, InputIterator l, size_type n,
   >                                        const hasher& hf, const allocator_type& a)
   >             : unordered_multimap(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_multimap(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_multimap(from_range, std::forward<R>(rg),
   >                                  n, hasher(), key_equal(), a) { }
   >         […]
   >         constexpr unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
   >         constexpr unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
   >           : unordered_multimap(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };
   >     }

4. Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2

   >     template<class InputIterator>
   >       constexpr unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       constexpr unordered_multimap(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_multimap(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     constexpr unordered_multimap(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

5. Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_set {
   >       public:
   >         […]
   >         constexpr unordered_set(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_set(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           constexpr unordered_set(InputIterator f, InputIterator l, size_type n,
   >                                   const allocator_type& a)
   >             : unordered_set(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
   >                                   const allocator_type& a)
   >             : unordered_set(f, l, n, hf, key_equal(), a) { }
   >         constexpr unordered_set(initializer_list<value_type> il, const allocator_type& a);
   >         constexpr unordered_set(initializer_list<value_type> il, size_type n,
   >                                 const allocator_type& a)
   >           : unordered_set(il, n, hasher(), key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_set(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
   >           -> unordered_set<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_set(InputIterator, InputIterator, Allocator)
   >           -> unordered_set<iter-value-type<InputIterator>,
   >                            hash<iter-value-type<InputIterator>>,
   >                            equal_to<iter-value-type<InputIterator>>,
   >                            Allocator>;

   >       template<class T, class Allocator>
   >         unordered_set(initializer_list<T>, Allocator)
   >           -> unordered_set<T, hash<T>, equal_to<T>, Allocator>;
   >     }

6. Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2

   >     template<class InputIterator>
   >       constexpr unordered_set(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       constexpr unordered_set(from_range_t, R&& rg, const allocator_type& a)
   >         : unordered_set(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     constexpr unordered_set(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

7. Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:

   >     namespace std {
   >       template<class Key,
   >                class Hash = hash<Key>,
   >                class Pred = equal_to<Key>,
   >                class Allocator = allocator<Key>> {
   >       class unordered_multiset {
   >       public:
   >         […]
   >         constexpr unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(n, hf, key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
   >         template<class InputIterator>
   >           constexpr unordered_multiset(InputIterator f, InputIterator l, size_type n,
   >                                        const allocator_type& a)
   >           : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
   >         template<class InputIterator>
   >           constexpr unordered_multiset(InputIterator f, InputIterator l, size_type n,
   >                                        const hasher& hf, const allocator_type& a)
   >           : unordered_multiset(f, l, n, hf, key_equal(), a) { }
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_multiset(from_range_t, R&& rg, const allocator_type& a);
   >         template<container-compatible-range<value_type> R>
   >           constexpr unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
   >             : unordered_multiset(from_range, std::forward<R>(rg),
   >                                  n, hasher(), key_equal(), a) { }
   >         […]
   >         constexpr unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
   >         constexpr unordered_multiset(initializer_list<value_type> il, size_type n,
   >                                      const allocator_type& a)
   >           : unordered_multiset(il, n, hasher(), key_equal(), a) { }
   >         […]
   >       };

   >       […]
   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;

   >       template<class T, class Hash, class Allocator>
   >         unordered_multiset(initializer_list<T>, typename see below ::size_type, Hash, Allocator)
   >           -> unordered_multiset<T, Hash, equal_to<T>, Allocator>;

   >       template<class InputIterator, class Allocator>
   >         unordered_multiset(InputIterator, InputIterator, Allocator)
   >           -> unordered_multiset<iter-value-type<InputIterator>,
   >                                 hash<iter-value-type<InputIterator>>,
   >                                 equal_to<iter-value-type<InputIterator>>,
   >                                 Allocator>;

   >       template<class T, class Allocator>
   >         unordered_multiset(initializer_list<T>, Allocator)
   >           -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
   >     }

8. Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2

   >     template<class InputIterator>
   >       constexpr unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
   >         : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { }

   >     template<container-compatible-range<value_type> R>
   >       constexpr unordered_multiset(from_range_t, R&& rg, const allocator_type& a))
   >         : unordered_multiset(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { }

   >     constexpr unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
   >       : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }

   > > -?- Remarks: The initial number of buckets supplied by the `size_type` argument is implementation-defined.

### 2714(i). `complex` stream extraction underspecified

Section: 29.4.6 [complex.ops] Status: Ready Submitter: Tim Song Opened: 2016-05-23 Last modified: 2026-06-09

Priority: 3

View all other issues in [complex.ops].

View all issues with Ready status.

Discussion:

The specification of `operator>>(istream&, complex<T>&)` is extremely short on details. It currently reads, in its entirety (29.4.6 [complex.ops]/12-15):

>     template<class T, class charT, class traits>
>     basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);

> > Effects: Extracts a complex number `x` of the form: `u`, `(u)`, or `(u,v)`, where `u` is the real part and `v` is the imaginary part (31.7.5.3 [istream.formatted]).

> > Requires: The input values shall be convertible to `T`.

> > If bad input is encountered, calls `is.setstate(ios_base::failbit)` (which may throw `ios::failure` (31.5.4.4 [iostate.flags])).

> > Returns: `is`.

> > Remarks: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions.

It is completely unclear:

- which "simpler extractions" are performed,
- how the extracted characters are matched to the special characters `'('`, `')'` and `','` (by `==`, or by `traits::eq`),
- what is left in the stream on failure. (For example, with `"(0, 0]"`, libstdc++ extracts the `]` while libc++ leaves it in the stream.)

Previous resolution [SUPERSEDED]

> Drafting note: the following wording is based on:
> - Characters are extracted using `operator>>` and compared using `traits::eq`.
> - Mismatched characters are returned to the stream.

This wording is relative to N4582.

1. Replace 29.4.6 [complex.ops]/12-15 with the following paragraphs:

   >     template<class T, class charT, class traits>
   >     basic_istream<charT, traits>&
   >     operator>>(basic_istream<charT, traits>& is, complex<T>& x);

   > > -?- Effects: First, extracts a character from `is`.

   > > - If the character extracted is equal to `is.widen('(')`, extracts an object `u` of type `T` from `is`, then extracts a character from `is`.
   > >   - If this character is equal to `is.widen(')')`, then assigns `complex<T>(u)` to `x`.
   > >   - Otherwise, if this character is equal to `is.widen(',')`, extracts an object `v` of type `T` from `is`, then extracts a character from `is`. If this character is equal to `is.widen(')')`, then assigns `complex<T>(u, v)` to `x`; otherwise returns the character to `is` and the extraction fails.
   > >   - Otherwise, returns the character to `is` and the extraction fails.
   > > - Otherwise, returns the character to `is`, extracts an object `u` of type `T` from `is`, and assigns `complex<T>(u)` to `x`.
   > > In the description above, characters are extracted from `is` as if by `operator>>` (31.7.5.3.3 [istream.extractors]), and returned to the stream as if by `basic_istream::putback` (31.7.5.4 [istream.unformatted]). Character equality is determined using `traits::eq`. An object `t` of type `T` is extracted from `is` as if by `is >> t`.

   > > If any extraction operation fails, no further operation is performed and the whole extraction fails.

   > > On failure, calls `is.setstate(ios_base::failbit)` (which may throw `ios::failure` (31.5.4.4 [iostate.flags])).

   > > -?- Returns: `is`.

   > > -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]

[2017-12-13 Tim Song adjusts the P/R to avoid relying on `putback`.]

[Brno 2026-06-09 Status changed: New → Ready.]

The relevant paragraphs are 9-13 now.

Proposed resolution:

> Drafting note: the following wording assumes that:
> - Characters are extracted using `operator>>` and compared using `traits::eq`.
> - Mismatched characters are not extracted.
> - `x` is assigned a value-initialized `complex` on failure for consistency with the arithmetic extractors (compare LWG 696(i)).

This wording is relative to N4778.

1. Replace 29.4.6 [complex.ops]/12-16 with the following paragraphs:

   >     template<class T, class charT, class traits>
   >     basic_istream<charT, traits>&
   >     operator>>(basic_istream<charT, traits>& is, complex<T>& x);

   > > -?- Effects: Let `PEEK(is)` be a formatted input function (31.7.5.3.1 [istream.formatted.reqmts]) of `is` that returns the next character that would be extracted from `is` by `operator>>`. [Note: The `sentry` object is constructed and destroyed, but the returned character is not extracted from the stream. — end note]

   > > - If `PEEK(is)` is not equal to `is.widen('(')`, extracts an object `u` of type `T` from `is`, and assigns `complex<T>(u)` to `x`.
   > > - Otherwise, extracts that character from `is`, then extracts an object `u` of type `T` from `is`, then:
   > >   - If `PEEK(is)` is equal to `is.widen(')')`, then extracts that character from `is` and assigns `complex<T>(u)` to `x`.
   > >   - Otherwise, if it is equal to `is.widen(',')`, then extracts that character from `is` and then extracts an object `v` of type `T` from `is`, then:
   > >     - If `PEEK(is)` is equal to `is.widen(')')`, then extracts that character from `is` and assigns `complex<T>(u, v)` to `x`.
   > >     - Otherwise, the extraction fails.
   > >   - Otherwise, the extraction fails.
   > > In the description above, characters are extracted from `is` as if by `operator>>` (31.7.5.3.3 [istream.extractors]), character equality is determined using `traits::eq`, and an object `t` of type `T` is extracted from `is` as if by `is >> t`.

   > > If any extraction operation fails, no further operation is performed and the whole extraction fails.

   > > On failure, assigns `complex<T>()` to `x` and calls `is.setstate(ios_base::failbit)` (which may throw `ios::failure` (31.5.4.4 [iostate.flags])).

   > > -?- Returns: `is`.

   > > -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]

### 2730(i). `numeric_limits` primary template definition

Section: 17.3.5 [numeric.limits] Status: Open Submitter: Richard Smith Opened: 2016-06-09 Last modified: 2023-04-18

Priority: 3

View all other issues in [numeric.limits].

View all issues with Open status.

Discussion:

I've received this report at the project editor mail alias, and it seems like it may be worthy of a LWG issue:

> I recently had this problem:

> - I was storing data in a vector of `__uint128_t`s
> - I used a sorting library which used `numeric_limits<T>::max()` as a sentinel value
> - GCC's libstdc++ provides a `numeric_limits` specialisation for that type, but
> - Clang's libc++ does not.

> This broke the sorting for me on different platforms, and it was quite difficult to determine why. If the default `numeric_limits` didn't default to `0`s and `false` values (18.3.2.4 of N4582), and instead `static_assert`ed, causing my code to not compile, I would have found the solution immediately.

> I know that `__uint128_t` is non-standard, so neither GCC nor Clang is doing the wrong thing nor the right thing here. I could just submit a patch to libc++ providing the specialisations, but it doesn't fix the problem at its core.

> I am wondering, what is the rationale behind the defaults being `0` and `false`? It seems like it is inviting a problem for any future numeric types, whether part of a library, compiler extension, and possibly even future updates to C++'s numeric types. I think it would be much better to prevent code that tries to use unspecified `numeric_limits` from compiling.

An alternative to this suggestion would be to still define the primary template, but not provide any of the members except `is_specialized`. Either way, this would make `numeric_limits` members SFINAEable.

Along the same lines, one might wonder why the members that only make sense for floating-point types are required to be defined to nonsense values for integer types.

[2016-11-12, Issaquah]

Sat PM: This looks like a good idea. Jonathan and Marshall will do post C++17 implementations and report back.

[2018-11 San Diego Thursday night issue processing]

See Walter's paper P0437 for ideas and/or future directions.

[2023-04]

See Walter's paper P1841 for the preferred direction.

Proposed resolution:

### 2751(i). `shared_ptr` deleter not specified to observe expired `weak_ptr` instances

Section: 20.3.2.2.3 [util.smartptr.shared.dest] Status: New Submitter: Aaron Jacobs Opened: 2016-07-21 Last modified: 2025-03-18

Priority: 4

View all other issues in [util.smartptr.shared.dest].

View all issues with New status.

Discussion:

The C++14 standard contains no language that guarantees the deleter run by a `shared_ptr` will see all associated `weak_ptr` instances as expired. For example, the standard doesn't appear to guarantee that the assertion in the following snippet won't fire:

>     std::weak_ptr<Foo> weak;
>     std::shared_ptr<Foo> strong{
>       new Foo,
>       [&weak] (Foo* f) {
>         assert(weak.expired());
>         delete f;
>       },
>     };

>     weak = strong;
>     strong.reset();

It seems clear that the intent is that associated `weak_ptr`s are expired, because otherwise `shared_ptr` deleters could resurrect a reference to an object that is being deleted.

Suggested fix: 20.3.2.2.3 [util.smartptr.shared.dest] should specify that the decrease in `use_count()` caused by the destructor is sequenced before the call to the deleter or the call to `delete p`.

[2016-11-08, Jonathan and STL suggest NAD]

STL and Jonathan feel that the example has unspecified behaviour, and the assertion is allowed to fire, and that's OK (the program's expectation is not reasonable). Otherwise it's necessary to move-construct a copy of the deleter and use that copy to destroy the owned pointer. We do not want to be required to do that.

See also 2262(i).

[2017-09-20, Jonathan comments]

I'd like to withdraw my NAD suggestion. The value of `use_count()` is already observable during the destructor via `shared_ptr` and `weak_ptr` objects that share ownership, so specifying when it changes ensures correct behaviour.

[2025-03-18, Jonathan comments]

See also 2907(i).

Proposed resolution:

### 2766(i). Swapping non-swappable types

Section: 22.3.3 [pairs.spec], 22.4.12 [tuple.special], 22.5.10 [optional.specalg], 22.6.10 [variant.specalg], 20.3.1.6 [unique.ptr.special], 23.3.3.4 [array.special], 23.6.3.6 [queue.special], 23.6.4.5 [priqueue.special], 23.6.6.7 [stack.special] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-08-15 Last modified: 2020-09-06

Priority: 3

View all other issues in [pairs.spec].

View all issues with New status.

Discussion:

Related: 2748(i) swappable traits for optionals, 2749(i) swappable traits for variants.

The adoption of P0185R1 "Adding [nothrow-]swappable traits" makes certain non-swappable types indirectly swappable. Consider a type defined as follows:

>     struct non_swappable {
>       friend void swap(non_swappable&, non_swappable&) = delete;
>     };

>     non_swappable ns1, ns2;
>     using std::swap;
>     swap(ns1, ns2); // ill-formed

>     static_assert(std::is_swappable_v<non_swappable> == false); // holds

Lvalues of type `non_swappable` are not swappable, as defined by 16.4.4.3 [swappable.requirements], overload resolution selects the deleted function. Consistently, `is_swappable_v<non_swappable>` yields false. It should be noted that since `non_swappable` is move constructible and move assignable, a qualified call to `std::swap` would be well-formed, even under P0185. Now consider the following snippet:

>     std::tuple<non_swappable> tns1, tns2;
>     using std::swap;
>     swap(tns1, tns2); // previously ill-formed, now well-formed

>     static_assert(std::is_swappable_v<std::tuple<non_swappable>> == false); // fires

Before P0185, this snippet would violate the implicit requirement of specialized swap for tuples that each tuple element be swappable. After P0185, this specialized swap overload for tuples would be SFINAEd away, resulting in overload resolution selecting the base swap overload, and performing the exchange via move construction and move assignment of tuples.

This issue affects all of `pair`, `tuple`, `unique_ptr`, `array`, `queue`, `priority_queue`, `stack`, and should eventually also apply to `optional` and `variant`. Previous resolution [SUPERSEDED]:

> This wording is relative to N4606, except when otherwise noted.

> 1. Modify 22.3.3 [pairs.spec] as indicated:

>    >     template<class T1, class T2> void swap(pair<T1, T2>& x, pair<T1, T2>& y)
>    >       noexcept(noexcept(x.swap(y)));

>    > > -7- Effects: As if by `x.swap(y)`.

>    > > -8- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<T1>` is `true` and `is_swappable_v<T2>` is `true`.

> 2. Modify 22.4.12 [tuple.special] as indicated:

>    >     template <class... Types>
>    >       void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);

>    > > -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<`Ti`>` is `true` for all `i`, where `0 <= i` and `i < sizeof...(Types)`. The expression inside `noexcept` is equivalent to:

>    > > >     noexcept(x.swap(y))

>    > > -2- Effects: As if by `x.swap(y)`.

> 3. Modify 20.3.1.6 [unique.ptr.special] as indicated:

>    >     template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;

>    > > -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<D>` is `true`.

>    > > -2- Effects: Calls `x.swap(y)`.

> 4. Modify 23.3.3.4 [array.special] as indicated:

>    >     template <class T, size_t N>
>    >       void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));

>    > > -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `N == 0` or `is_swappable_v<T>` is `true`.

>    > > -2- Effects: As if by `x.swap(y)`.

>    > > […]

> 5. Modify 23.6.3.6 [queue.special] as indicated:

>    >     template <class T, class Container>
>    >       void swap(queue<T, Container>& x, queue<T, Container>& y) noexcept(noexcept(x.swap(y)));

>    > > -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<Container>` is `true`.

>    > > -2- Effects: As if by `x.swap(y)`.

> 6. Modify 23.6.4.5 [priqueue.special] as indicated:

>    >     template <class T, class Container, class Compare>
>    >       void swap(priority_queue<T, Container, Compare>& x,
>    >                 priority_queue<T, Container, Compare>& y) noexcept(noexcept(x.swap(y)));

>    > > -1- `Remarks:` This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<Container>` is `true` and `is_swappable_v<Compare>` is `true`.

>    > > -2- Effects: As if by `x.swap(y)`.

> 7. Modify 23.6.6.7 [stack.special] as indicated:

>    >     template <class T, class Container>
>    >       void swap(stack<T, Container>& x, stack<T, Container>& y) noexcept(noexcept(x.swap(y)));

>    > > -1- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_swappable_v<Container>` is `true`.

>    > > -2- Effects: As if by `x.swap(y)`.

> 8. Modify 22.5.10 [optional.specalg] as indicated:

>    > This change should be performed if and only if LWG 2748(i) is accepted and is against the wording of 2748(i):

>    >     template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));

>    > > -1- Effects: Calls `x.swap(y)`.

>    > > -2- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_move_constructible_v<T>` is `true` and `is_swappable_v<T>` is `true`.

> 9. Modify 22.6.10 [variant.specalg] as indicated:

>    > This change should be performed if and only if LWG 2749(i) is accepted and is against the wording of 2749(i):

>    >     template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);

>    > > -1- Effects: Equivalent to `v.swap(w)`.

>    > > -2- Remarks: This function shall not participate in overload resolutionbe defined as deleted unless `is_move_constructible_v<Ti> && is_swappable_v<Ti>` is `true` for all `i`. The expression inside `noexcept` is equivalent to `noexcept(v.swap(w))`.

[2019-04-17 Jonathan updates proposed resolution based on Ville's 2016-11-17 observation that the container adaptors always require swappable sequences anyway. The new proposed resolution is based on the latest WP, "de-shalled", and Remarks elements are repositioned after the Effects.]

Proposed resolution:

This wording is relative to N4810.

1. Modify 22.3.3 [pairs.spec] as indicated:

   >     template<class T1, class T2>
   >       constexpr void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));

   > > -7- Effects: As if by `x.swap(y)`.

   > > -8- Remarks: This function shall not participate in overload resolutionis defined as deleted unless `is_swappable_v<T1>` is `true` and `is_swappable_v<T2>` is `true`.

2. Modify 22.4.12 [tuple.special] as indicated:

   >     template <class... Types>
   >       constexpr void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);

   > > -?- Effects: As if by `x.swap(y)`.

   > > -1- Remarks: This function shall not participate in overload resolutionis defined as deleted unless `is_swappable_v<`Ti`>` is `true` for all `i`, where `0 <= i` and `i < sizeof...(Types)`. The expression inside `noexcept` is equivalent to:

   > > >     noexcept(x.swap(y))

   > > -2- Effects: As if by `x.swap(y)`.

3. Modify 22.5.10 [optional.specalg] as indicated:

   >     template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));

   > > -1- Effects: Calls `x.swap(y)`.

   > > -2- Remarks: This function shall not participate in overload resolutionis defined as deleted unless `is_move_constructible_v<T>` is `true` and `is_swappable_v<T>` is `true`.

4. Modify 22.6.10 [variant.specalg] as indicated:

   >     template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);

   > > -1- Effects: Equivalent to `v.swap(w)`.

   > > -2- Remarks: This function shall not participate in overload resolutionis defined as deleted unless `is_move_constructible_v<Ti> && is_swappable_v<Ti>` is `true` for all `i`. The expression inside `noexcept` is equivalent to `noexcept(v.swap(w))`.

5. Modify 20.3.1.6 [unique.ptr.special] as indicated:

   >     template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;

   > > -?- Effects: Calls `x.swap(y)`.

   > > -1- Remarks: This function shall not participate in overload resolutionis defined as deleted unless `is_swappable_v<D>` is `true`.

   > > -2- Effects: Calls `x.swap(y)`.

6. Modify 23.3.3.4 [array.special] as indicated:

   >     template <class T, size_t N>
   >       void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));

   > > -1- Constraints: `N == 0` or `is_swappable_v<T>` is `true`.

   > > -2- Effects: As if by `x.swap(y)`.

   > > -3- Complexity: Linear in `N`.

   > > -?- Remarks: This function is defined as deleted unless `N == 0` or `is_swappable_v<T>` is `true`.

### 2811(i). "Selected constructor" wording is incorrect for `optional`/`variant`/`any`

Section: 22.5.3.2 [optional.ctor], 22.5.3.4 [optional.assign], 22.6.3.2 [variant.ctor], 22.7.4.2 [any.cons], 22.7.4.4 [any.modifiers] Status: Ready Submitter: Tim Song Opened: 2016-10-29 Last modified: 2026-06-10

Priority: 3

View all other issues in [optional.ctor].

View all issues with Ready status.

Discussion:

Throughout `optional`/`variant`/`any`'s specification references are made to "the selected constructor of `T`". For example, 22.5.3.2 [optional.ctor]/16 says of the constructor from `const T&`:

> -16- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

Similarly, the in-place constructor has this wording (22.5.3.2 [optional.ctor]/25-26):

> -25- Throws: Any exception thrown by the selected constructor of `T`.

> -26- Remarks: If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

If `T` is a scalar type, it has no constructor at all. Moreover, even for class types, the in-place constructor wording ignores any implicit conversion done on the argument before the selected constructor is called, which 1) may not be valid in constant expressions and 2) may throw an exception; such exceptions aren't thrown "by the selected constructor of `T`" but outside it.

The wording should probably be recast to refer to the entire initialization.

[Issues Telecon 16-Dec-2016]

Priority 3; Jonathan to provide wording.

[2020-06-11; Nina Dinka Ranns comments and provides initial wording]

This wording depends on the current resolution for LWG 2833(i), which covers the `constexpr` portion of this issue.

Previous resolution [SUPERSEDED]

This wording is relative to N4861.

1. Modify 22.5.3.2 [optional.ctor] as indicated:

   >     constexpr optional(const optional& rhs);

   > > -3- […]

   > > -4- […]

   > > -5- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     constexpr optional(optional&& rhs) noexcept(see below);

   > > -7- […]

   > > […]

   > > -10- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);

   > > -12- […]

   > > […]

   > > -15- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > -17- […]

   > > […]

   > > -20- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U = T> constexpr explicit(see below) optional(U&& v);

   > > -22- […]

   > > […]

   > > -25- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U> explicit(see below) optional(const optional<U>& rhs);

   > > -27- […]

   > > […]

   > > -30- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U> explicit(see below) optional(optional<U>&& rhs);

   > > -32- […]

   > > […]

   > > -35- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

2. Modify 22.6.3.2 [variant.ctor] as indicated:

   >     template<class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);

   > > -20- […]

   > > […]

   > > -23- Throws: Any exception thrown by calling the selected constructor of `T` the initialization of the contained value.

   >     template<class T, class U, class... Args>
   >       constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);

   > > -25- […]

   > > […]

   > > -28- Throws: Any exception thrown by calling the selected constructor of `T` the initialization of the contained value.

   >     template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);

   > > -30 […]

   > > […]

   > > -33- Throws: Any exception thrown by calling the selected constructor of `TI` the initialization of the contained value.

3. Modify 22.7.4.2 [any.cons] as indicated:

   >     any(const any& other);

   > > -2- Effects: […]

   > > -3- Throws: Any exceptions arising from calling the selected constructor forthrown by the initialization of the contained value.

   > […]

   >     template<class T>
   >       any(T&& value);

   > > -5- […]

   > > […]

   > > -9- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   >     template<class T, class... Args>
   >       explicit any(in_place_type_t<T>, Args&&... args);

   > > -10- […]

   > > […]

   > > -15- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   >     template<class T, class U, class... Args>
   >       explicit any(in_place_type_t<T>, initializer_list<U> il, Args&&... args);

   > > -16- […]

   > > […]

   > > -21- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

4. Modify 22.7.4.4 [any.modifiers] as indicated:

   >     template<class T, class... Args>
   >       decay_t<T>& emplace(Args&&... args);

   > > -1- […]

   > > […]

   > > -7- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   > > -8- […]

   >     template<class T, class U, class... Args>
   >       decay_t<T>& emplace(initializer_list<U> il, Args&&... args);

   > > -9- […]

   > > […]

   > > -15- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

[2026-06-10; Tim rebases and provides new wording]

The `variant` part has been fixed by LWG 4460(i).

[Brno 2026-06-10; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N5060.

1. Modify 22.5.3.2 [optional.ctor] as indicated:

   >     constexpr optional(const optional& rhs);

   > > […]

   > > -6- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     constexpr optional(optional&& rhs) noexcept(see below);

   > > […]

   > > -11- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);

   > > […]

   > > -16- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > […]

   > > -21- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U = T> constexpr explicit(see below) optional(U&& v);

   > > […]

   > > -26- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U> explicit(see below) optional(const optional<U>& rhs);

   > > […]

   > > -31- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

   >     template<class U> explicit(see below) optional(optional<U>&& rhs);

   > > […]

   > > -36- Throws: Any exception thrown by the selected constructor of `T` initialization of the contained value.

2. Modify 22.7.4.2 [any.cons] as indicated:

   >     any(const any& other);

   > > -2- Effects: […]

   > > -3- Throws: Any exceptions arising from calling the selected constructor forthrown by the initialization of the contained value.

   > […]

   >     template<class T>
   >       any(T&& value);

   > > -5- […]

   > > […]

   > > -9- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   >     template<class T, class... Args>
   >       explicit any(in_place_type_t<T>, Args&&... args);

   > > -10- […]

   > > […]

   > > -15- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   >     template<class T, class U, class... Args>
   >       explicit any(in_place_type_t<T>, initializer_list<U> il, Args&&... args);

   > > -16- […]

   > > […]

   > > -21- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

3. Modify 22.7.4.4 [any.modifiers] as indicated:

   >     template<class T, class... Args>
   >       decay_t<T>& emplace(Args&&... args);

   > > -1- […]

   > > […]

   > > -7- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

   > > -8- […]

   >     template<class T, class U, class... Args>
   >       decay_t<T>& emplace(initializer_list<U> il, Args&&... args);

   > > -9- […]

   > > […]

   > > -15- Throws: Any exception thrown by the selected constructor of `VT` initialization of the contained value.

### 2815(i). `quick_exit` can deadlock

Section: 17.5 [support.start.term] Status: New Submitter: Jean-François Bastien Opened: 2016-11-07 Last modified: 2020-09-06

Priority: 3

View other active issues in [support.start.term].

View all other issues in [support.start.term].

View all issues with New status.

Discussion:

While SG1 was processing NB comments CA1 and LATE2 regarding P0270R1, we decided to remove the proposed guarantee that `quick_exit` be made signal safe.

Our reasoning is that functions registered with `at_quick_exit` aren't forbidden from calling `quick_exit`, but `quick_exit` implementations likely acquire some form of a lock before processing all registered functions (because a note forbids the implementation from introducing data races).

The following code can therefore deadlock:

>     #include <cstdlib>

>     int main()
>     {
>       std::at_quick_exit([] () { std::quick_exit(0); });
>       std::quick_exit(1);
>       return 0;
>     }

The same applies if a function registered in `at_quick_exit` handles a signal, and that signal calls `quick_exit`. SG1 believes that both issues (same thread deadlock, and signal deadlock) can be resolved in the same manner. Either:

1. Specify that calling `quick_exit` while servicing `quick_exit` is undefined; or
2. Specifying that calling `quick_exit` while servicing `quick_exit` is defined to not deadlock, and instead calls `_Exit` without calling further registered functions.

Option 2. seems preferable, and can be implemented along the lines of:

>     #include <array>
>     #include <atomic>
>     #include <cstddef>

>     namespace {

>       typedef void (*func)();

>       std::array<func, 32> quick_exit_functions;

>       const auto* quick_exit_functions_ptr = &quick_exit_functions;

>       std::atomic_flag lock = ATOMIC_FLAG_INIT;

>       struct scope
>       {
>         scope() { while (lock.test_and_set(std::memory_order_acquire)) ; }
>         ~scope() { lock.clear(std::memory_order_release); }
>       };

>     }

>     namespace std {

>       extern "C" void quick_exit(int status) noexcept
>       {
>         decltype(quick_exit_functions_ptr) f;
>         {
>           scope s;
>           f = quick_exit_functions_ptr;
>           quick_exit_functions_ptr = nullptr;
>         }
>         if (f) {
>           size_t pos = f->size();
>           while (pos > 0)
>             (*f)[--pos]();
>         }
>         _Exit(status);
>       }

>       extern "C++" int at_quick_exit(func f) noexcept
>       {
>         scope s;
>         if (!quick_exit_functions_ptr || quick_exit_functions.size() == quick_exit_functions.max_size())
>           return -1;
>         quick_exit_functions[quick_exit_functions.size()] = f;
>         return 0;
>       }

>     }

Ideally, the resolution would also add back the wording which SG1 dropped from P0270R1:

> Add at new element to the end of 17.5 [support.start.term] p13 (`quick_exit()`):

> > Remarks: The function `quick_exit()` is signal-safe (17.14.4 [csignal.syn]). [Note: It might still be unsafe to call `quick_exit()` from a handler, because the functions registered with `at_quick_exit()` might not be signal-safe. — end note]

[Issues Telecon 16-Dec-2016]

Priority 3

Proposed resolution:

This wording is relative to N4606.

1. Add at new element to the end of 17.5 [support.start.term] p13 (`quick_exit()`):

   >     [[noreturn]] void quick_exit(int status) noexcept;

   > > -13- Effects: Functions registered by calls to `at_quick_exit` are called in the reverse order of their registration, except that a function shall be called after any previously registered functions that had already been called at the time it was registered. Objects shall not be destroyed as a result of calling `quick_exit`. If control leaves a registered function called by `quick_exit` because the function does not provide a handler for a thrown exception, `std::terminate()` shall be called. [Note: `at_quick_exit` may call a registered function from a different thread than the one that registered it, so registered functions should not rely on the identity of objects with thread storage duration. — end note] After calling registered functions, `quick_exit` shall call `_Exit(status)`. [Note: The standard file buffers are not flushed. See: ISO C 7.22.4.5. — end note]

   > > -?- Remarks: The function `quick_exit()` is signal-safe (17.14.4 [csignal.syn]). [Note: It might still be unsafe to call `quick_exit()` from a handler, because the functions registered with `at_quick_exit()` might not be signal-safe. — end note]

### 2819(i). Unspecified Return type: elements

Section: 32.2.5 [thread.req.lockable], 32.6.4 [thread.mutex.requirements] Status: Ready Submitter: Agustín K-ballo Bergé Opened: 2016-11-12 Last modified: 2026-06-10

Priority: 3

View all issues with Ready status.

Discussion:

The current draft contains 14 occurrences of a Return type: clause. That clause is not covered by 16.3.2.4 [structure.specifications] p3. This was reported as editorial request #266.

[Issues Telecon 16-Dec-2016]

Priority 3; Jonathan to provide wording.

[12-May-2020, Jonathan provides wording to correct the 13 occurrences.]

Previous resolution [SUPERSEDED]

This wording is relative to N4681.

1. Modify 32.2.5.3 [thread.req.lockable.req] as indicated:

   > -1- A type `L` meets the Cpp17Lockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed, have type `bool`, and have the specified semantics (`m` denotes a value of type `L`).

   > `m.try_lock()`

   > > -2- Effects: [...]

   > > -3- Return type: `bool`.

   > > -4- Returns: `true` if the lock was acquired, `false` otherwise.

2. Modify 32.2.5.4 [thread.req.lockable.timed] as indicated:

   > -1- A type `L` meets the Cpp17TimedLockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed, have type `bool`, and have the specified semantics (`m` denotes a value of type `L`, `rel_time` denotes a value of an instantiation of `duration` (30.5 [time.duration]), and `abs_time` denotes a value of an instantiation of `time_point` (30.6 [time.point])).

   > `m.try_lock_for(rel_time)`

   > > -2- Effects: [...]

   > > -3- Return type: `bool`.

   > > -4- Returns: `true` if the lock was acquired, `false` otherwise.

   > `m.try_lock_until(abs_time)`

   > > -2- Effects: [...]

   > > -3- Return type: `bool`.

   > > -4- Returns: `true` if the lock was acquired, `false` otherwise.

3. Modify 32.6.4.2 [thread.mutex.requirements.mutex] as indicated:

   > -6- The expression `m.lock()` is well-formed, has type `void`, and has the following semantics:

   > > -7- Preconditions: [...]

   > > -8- Effects: [...]

   > > -9- Postconditions: [...]

   > > -10- Return type: `void`.

   > > -11- Synchronization: [...]

   > > -12- Throws: [...]

   > > -13- Error conditions: [...]

   > -14- The expression `m.try_lock()` is well-formed, has type `bool`, and has the following semantics:

   > > -15- Preconditions: [...]

   > > -16- Effects: [...]

   > > -17- Return type: `bool`.

   > > -18- Returns: `true` if ownership of the mutex was obtained for the calling thread, otherwise `false`.

   > > -19- Synchronization: [...]

   > > -20- Throws: Nothing.

   > -21- The expression `m.unlock()` is well-formed, has type `void`, and has the following semantics:

   > > -22- Preconditions: [...]

   > > -23- Effects: [...]

   > > -24- Return type: `void`.

   > > -25- Synchronization: [...]

   > > -26- Throws: Nothing.

4. Modify 32.6.4.3 [thread.timedmutex.requirements] as indicated:

   > -1- The timed mutex types are the standard library types [...]

   > -2- The timed mutex types meet the Cpp17TimedLockable requirements (32.2.5.4 [thread.req.lockable.timed]).

   > -3- The expression `m.try_lock_for(rel_time)` is well-formed, has type `bool`, and has the following semantics:

   > > -4- Preconditions: [...]

   > > -5- Effects: [...]

   > > -6- Return type: `bool`.

   > > -7- Returns: `true` if the shared lock was acquired, `false` otherwise.

   > > -8- Synchronization: [...]

   > > -9- Throws: [...]

   > -10- The expression `m.try_lock_until(abs_time)` is well-formed, has type `bool`, and has the following semantics:

   > > -11- Preconditions: [...]

   > > -12- Effects: [...]

   > > -13- Return type: `bool`.

   > > -14- Returns: `true` if ownership was obtained, otherwise `false`.

   > > -15- Synchronization: [...]

   > > -16- Throws: [...]

5. Modify 32.6.4.4 [thread.sharedmutex.requirements] as indicated:

   > -1- The standard library types `shared_mutex` and `shared_timed_mutex` are shared mutex types. [...]

   > -2- In addition to the exclusive lock ownership mode [...]

   > -3- The expression `m.lock_shared()` is well-formed, has type `void`, and has the following semantics:

   > > -4- Preconditions: [...]

   > > -5- Effects: [...]

   > > -6- Postconditions: [...]

   > > -7- Return type: `void`.

   > > -8- Synchronization: [...]

   > > -9- Throws: [...]

   > > -10- Error conditions: [...]

   > -11- The expression `m.unlock_shared()` is well-formed, has type `void`, and has the following semantics:

   > > -12- Preconditions: [...]

   > > -13- Effects: [...]

   > > -14- Return type: `void`.

   > > -15- Synchronization: [...]

   > > -16- Throws: [...]

   > -17- The expression `m.try_lock_shared()` is well-formed, has type `bool`, and has the following semantics:

   > > -18- Preconditions: [...]

   > > -19- Effects: [...]

   > > -20- Return type: `bool`.

   > > -21- Returns: `true` if the shared ownership lock was acquired, `false` otherwise.

   > > -22- Synchronization: [...]

   > > -23- Throws: [...]

6. Modify 32.6.4.5 [thread.sharedtimedmutex.requirements] as indicated:

   > -1- The standard library type `shared_timed_mutex` is a shared timed mutex type. [...]

   > -2- The expression `m.try_lock_shared_for(rel_time)` is well-formed, has type `bool`, and has the following semantics:

   > > -3- Preconditions: [...]

   > > -4- Effects: [...]

   > > -5- Return type: `bool`.

   > > -6- Returns: `true` if the shared lock was acquired, `false` otherwise.

   > > -7- Synchronization: [...]

   > > -8- Throws: [...]

   > -9- The expression `m.try_lock_shared_until(abs_time)` is well-formed, has type `bool`, and has the following semantics:

   > > -10- Preconditions: [...]

   > > -11- Effects: [...]

   > > -12- Return type: `bool`.

   > > -13- Returns: `true` if the shared lock was acquired, `false` otherwise.

   > > -14- Synchronization: [...]

   > > -15- Throws: [...]

[2022-11-06; Daniel comments and provides alternative wording]

Now that we have the new element Result: specified in 16.3.2.4 [structure.specifications], we can simply replace all occurrences of the Return type: by this element.

[Brno 2026-06-10; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N4917.

1. Modify 32.2.5.3 [thread.req.lockable.req] as indicated:

   > -1- A type `L` meets the Cpp17Lockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed and have the specified semantics (`m` denotes a value of type `L`).

   > `m.try_lock()`

   > > -2- Effects: [...]

   > > -3- Return typeResult: `bool`.

   > > -4- Returns: `true` if the lock was acquired, otherwise `false`.

2. Modify 32.2.5.4 [thread.req.lockable.timed] as indicated:

   > -1- A type `L` meets the Cpp17TimedLockable requirements if it meets the Cpp17Lockable requirements and the following expressions are well-formed and have the specified semantics (`m` denotes a value of type `L`, `rel_time` denotes a value of an instantiation of `duration` (30.5 [time.duration]), and `abs_time` denotes a value of an instantiation of `time_point` (30.6 [time.point])).

   > `m.try_lock_for(rel_time)`

   > > -2- Effects: [...]

   > > -3- Return typeResult: `bool`.

   > > -4- Returns: `true` if the lock was acquired, otherwise `false`.

   > `m.try_lock_until(abs_time)`

   > > -5- Effects: [...]

   > > -6- Return typeResult: `bool`.

   > > -7- Returns: `true` if the lock was acquired, otherwise `false`.

3. Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:

   > -5- The expression `m.lock()` is well-formed and has the following semantics:

   > > -6- Preconditions: [...]

   > > -7- Effects: [...]

   > > -8- Synchronization: [...]

   > > -9- Postconditions: [...]

   > > -10- Return typeResult: `void`.

   > > -11- Throws: [...]

   > > -12- Error conditions: [...]

   > -13- The expression `m.try_lock()` is well-formed and has the following semantics:

   > > -14- Preconditions: [...]

   > > -15- Effects: [...]

   > > -16- Synchronization: [...]

   > > -17- Return typeResult: `bool`.

   > > -18- Returns: `true` if ownership was obtained, otherwise `false`.

   > > -19- Throws: Nothing.

   > -20- The expression `m.unlock()` is well-formed and has the following semantics:

   > > -21- Preconditions: [...]

   > > -22- Effects: [...]

   > > -23- Return typeResult: `void`.

   > > -24- Synchronization: [...]

   > > -25- Throws: Nothing.

4. Modify 32.6.4.3.1 [thread.timedmutex.requirements.general] as indicated:

   > -1- The timed mutex types are the standard library types […]

   > -2- The expression `m.try_lock_for(rel_time)` is well-formed and has the following semantics:

   > > -3- Preconditions: [...]

   > > -4- Effects: [...]

   > > -5- Synchronization: [...]

   > > -6- Return typeResult: `bool`.

   > > -7- Returns: `true` if ownership was obtained, otherwise `false`.

   > > -8- Throws: [...]

   > -9- The expression `m.try_lock_until(abs_time)` is well-formed and has the following semantics:

   > > -10- Preconditions: [...]

   > > -11- Effects: [...]

   > > -12- Synchronization: [...]

   > > -13- Return typeResult: `bool`.

   > > -14- Returns: `true` if ownership was obtained, otherwise `false`.

   > > -15- Throws: [...]

5. Modify 32.6.4.4.1 [thread.sharedmutex.requirements.general] as indicated:

   > -1- The standard library types `shared_mutex` and `shared_timed_mutex` are shared mutex types. [...]

   > -2- In addition to the exclusive lock ownership mode [...]

   > -3- The expression `m.lock_shared()` is well-formed and has the following semantics:

   > > -4- Preconditions: [...]

   > > -5- Effects: [...]

   > > -6- Synchronization: [...]

   > > -7- Postconditions: [...]

   > > -8- Return typeResult: `void`.

   > > -9- Throws: [...]

   > > -10- Error conditions: [...]

   > -11- The expression `m.unlock_shared()` is well-formed and has the following semantics:

   > > -12- Preconditions: [...]

   > > -13- Effects: [...]

   > > -14- Return typeResult: `void`.

   > > -15- Synchronization: [...]

   > > -16- Throws: [...]

   > -17- The expression `m.try_lock_shared()` is well-formed and has the following semantics:

   > > -18- Preconditions: [...]

   > > -19- Effects: [...]

   > > -20- Synchronization: [...]

   > > -21- Return typeResult: `bool`.

   > > -22- Returns: `true` if the shared lock was acquired, otherwise `false`.

   > > -23- Throws: [...]

6. Modify 32.6.4.5.1 [thread.sharedtimedmutex.requirements.general] as indicated:

   > -1- The standard library type `shared_timed_mutex` is a shared timed mutex type. [...]

   > -2- The expression `m.try_lock_shared_for(rel_time)` is well-formed and has the following semantics:

   > > -3- Preconditions: [...]

   > > -4- Effects: [...]

   > > -5- Synchronization: [...]

   > > -6- Return typeResult: `bool`.

   > > -7- Returns: `true` if the shared lock was acquired, otherwise `false`.

   > > -8- Throws: [...]

   > -9- The expression `m.try_lock_shared_until(abs_time)` is well-formed and has the following semantics:

   > > -10- Preconditions: [...]

   > > -11- Effects: [...]

   > > -12- Synchronization: [...]

   > > -13- Return typeResult: `bool`.

   > > -14- Returns: `true` if the shared lock was acquired, otherwise `false`.

   > > -15- Throws: [...]

### 2823(i). `std::array` initialization is still not permissive enough

Section: 23.3.3.1 [array.overview] Status: Open Submitter: Robert Haberlach Opened: 2016-11-16 Last modified: 2018-03-19

Priority: 3

View other active issues in [array.overview].

View all other issues in [array.overview].

View all issues with Open status.

Discussion:

LWG 2590(i)'s resolution is incomplete:

>     std::array<int, 1> arr{{0}};

should be fine, but isn't guaranteed, since `{0}` has no type. We should rather go for implicit conversion:

> An array is an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up to `N` elements whose types are convertible to `T` that can be implicitly converted to `T`.

[2016-11-26, Tim Song comments]

This is not possible as written, because due to the brace elision rules for aggregate initialization, `std::array<int, 2> arr{{0}, {1}};` will never work: the `{0}` is taken as initializing the inner array, and the `{1}` causes an error.

[2017-01-27 Telecon]

Priority 2; consensus is that the P/R is not quite right.

[2018-3-14 Wednesday evening issues processing; priority to 3; move to Open]

Jens: There's nothing you can do about the double braces in std::array. That's a core thing.

STL to write paper to resolve this.

Proposed resolution:

This wording is relative to N4606.

1. Change 23.3.3.1 [array.overview] p2 as indicated:

   > -2- An `array` is an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up to `N` elements whose types are convertiblethat can be implicitly converted to `T`.

### 2827(i). `is_trivially_constructible` and non-trivial destructors

Section: 21.3.5.4 [meta.unary.prop] Status: New Submitter: Richard Smith Opened: 2016-11-17 Last modified: 2023-05-25

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

>     struct S
>     {
>       ~S(); // non-trivial
>     };

>     static_assert(std::is_trivially_constructible<S>::value, "");

Should the assert pass? Implementations disagree.

Per 21.3.5.4 [meta.unary.prop]'s Table 38, this trait looks at whether the following variable definition is known to call no operation that is not trivial:

>     S t(create<Args>()...);

... where `Args` is an empty pack in this case. That variable definition results in a call to the `S` destructor. Should that call be considered by the trait?

[2017-01-27 Telecon]

Priority 3

This issue interacts with 2116(i)

[2020-01-24; Peter Dimov comments]

`std::is_trivially_copy_constructible_v<D>`, where `D` is

>     struct D
>     {
>       ~D() {}
>     };

reports `false`. This is because the definition of `is_trivially_copy_constructible` requires the invented variable definition `T t(declval<Args>()...);`, which in our case is `D t(declval<D>());`, to not call any nontrivial operations.

This is interpreted by implementations to include the destructor call, presumably for consistency with `is_nothrow_copy_constructible`. But that's wrong; the copy constructor is trivial.

As a consequence, `variant<D>` also doesn't have a trivial copy constructor, which causes (completely unnecessary) inefficiencies when said `variant` is copied.

[2023-05-25; May 2023 mailing]

Alisdair provided P2842R0.

Proposed resolution:

### 2829(i). LWG 2740 leaves behind vacuous words

Section: 22.5.3.7 [optional.observe] Status: Open Submitter: Richard Smith Opened: 2016-11-24 Last modified: 2020-06-13

Priority: 2

View other active issues in [optional.observe].

View all other issues in [optional.observe].

View all issues with Open status.

Discussion:

After applying LWG 2740(i), we have:

>     constexpr const T* operator->() const;
>     constexpr T* operator->();

> > -1- Requires: `*this` contains a value.

> > -2- Returns: `val`.

> > -3- Throws: Nothing.

> > -4- Remarks: These functions shall be `constexpr` functions.

Paragraph 4 is completely superfluous. We already said these functions were `constexpr` in the synopsis. Can it be removed?

[Issues Telecon 16-Dec-2016]

Priority 2

Jonathan notes: Although Richard is correct, I suggest we don't strike the paragraph, so that we remember to fix it as part of 2833(i), when we know how to say this properly.

[2018-06 Rapperswil Thursday issues processing]

Status to Open; also see 7.7 [expr.const]/6 and 2289(i).

[2020-06-08 Nina Dinka Ranns comments]

The revised wording provided by LWG 2833(i) should resolve this issue as well.

Proposed resolution:

### 2833(i). Library needs to specify what it means when it declares a function `constexpr`

Section: 22.6.3.2 [variant.ctor] Status: Open Submitter: Richard Smith Opened: 2016-11-28 Last modified: 2026-06-11

Priority: 2

View other active issues in [variant.ctor].

View all other issues in [variant.ctor].

View all issues with Open status.

Discussion:

The library has lots of functions declared `constexpr`, but it's not clear what that means. The `constexpr` keyword implies that there needs to be some invocation of the function, for some set of template arguments and function arguments, that is valid in a constant expression (otherwise the program would be ill-formed, with no diagnostic required), along with a few side conditions. I suspect the library intends to require something a lot stronger than that from implementations (something along the lines of "all calls that could reasonably be constant subexpressions are in fact constant subexpressions, unless otherwise stated").

[variant.ctor]/1 contains this, which should also be fixed:

> "This function shall be `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a `constexpr` function."

This is the wrong constraint: instead of constraining whether the function is `constexpr`, we should constrain whether a call to it is a constant subexpression.

Daniel:

This is has some considerable overlap with LWG 2289(i) but is phrased in a more general way.

[2016-12-16, Issues Telecon]

Priority 2; this is also the general case of 2829(i).

[2017-02-20, Alisdair comments and suggests concrete wording]

Below is is draft wording I was working on at Issaquah to try to address both issues.

[2017-11 Albuquerque Wednesday issue processing]

Status to Open; really needs a paper.

STL says "What about `plus<T>`?" `plus<int>` needs to be usable in a constexpr context, but `plus<string>` can't be.

[2017-11 Albuquerque Saturday issues processing]

Geoffrey to write a paper resolving this.

[2018-06 Rapperswil Thursday issues processing]

Geoffrey has been unable to write this paper due to time constraints. He wrote up his progress here. Daniel has offered to help someone to write this paper; he's willing to be a co-author.

[2018-08-23 Batavia Issues processing]

Michael Wong to investigate.

Previous resolution [SUPERSEDED]

This wording is relative to N4640.

1. Modify 16.4.6.7 [constexpr.functions] as indicated:

   > ### 17.6.5.6 `constexpr` functions and constructors [constexpr.functions]

   > -1- This International Standard explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). If the specification for a templated entity requires that it shall be a `constexpr` templated entity, then that templated entity shall be usable in a constant expression.. An implementation shall notmay declare anyadditional standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of `constexpr` functions or constructors an implementation shall provide corresponding definitions.

[2020-06-08 Nina Dinka Ranns comments and provides alternative wording]

The revised wording draft also resolves LWG 2289(i), LWG 2829(i), and LWG 3215(i).

Previous resolution [SUPERSEDED]

This wording is relative to N4861.

1. 1. Modify 16.4.6.7 [constexpr.functions] as indicated:

   > -1- This document explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.

   > -?- Let `F` denote a standard library function template or member function of a class template. If the specification of `F` declares it to be `constexpr`, unless otherwise specified, then `F` can be used in a constant expression if and only if all the expressions that are evaluated as specified in the description of `F`'s semantics can be used in a constant expression.

2. 2. - 10. [… Remainder of this superseded resolution is the same as the next resolution below this one …]

[2020-10-02 Jens Maurer improves wording]

Specifically the wording for 16.4.6.7 [constexpr.functions] needs improvement and is updated below.

[2020-10-02 Tim Song comments]

The new wording doesn't cover the following example:

>     // global scope
>     int x;
>     int y;

>     constexpr int j = (std::swap(x, y), 0); // error

- `swap` is a "standard library function template...declared `constexpr`"

- `x` and `y` are (lvalue) constant expressions

- `std::swap(x, y)` is plainly not a constant expression

[2020-10-04 Jens Maurer comments]

Yes, we're still lacking text for that (and maybe Nina's old text helps for that).

[2020-12-14; Jiang An comments]

The item "constexpr functions" is also used in 23.2.2 [container.requirements.general]/14 and 24.3.1 [iterator.requirements.general]/16, and such usage should also be modified by this issue here.

[St. Louis 2024-06-24; Re-confirmed Tim's previous observation, new P/R needed. Jens says there are two ways that `swap` could work, and the library doesn't actually say how it does what it does, so it's not possible for a reader to know whether they can expect it to be usable in a constant expression. ]

[2025-08-22; Discussion of editorial issue #4954]

During the discussion of editorial issue #4954 it has been suggested that this issue here should also attempt to find a replacement for the definition of the term "constant initializer" which got lost by CWG 2366 but is still referenced by 24.6.2.2 [istream.iterator.cons] p3.

[Brno 2026-06-11; needs to be rebased, especially if P3367 is approved.]

Proposed resolution:

This wording is relative to N4861.

1. Modify 16.4.6.7 [constexpr.functions] as indicated:

   > -1- This document explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.

   > -?- Let `F` denote a standard library function template or member function of a class template declared `constexpr`. Unless otherwise specified, a function call expression (7.6.1.3 [expr.call]) whose postfix-expression names `F` is a constant expression if all of the argument subexpressions are constant expressions.

2. Modify 22.3.2 [pairs.pair] as indicated:

   > -2- The defaulted move and copy constructors, respectively, of `pair` is a constexpr functioncan be used in a constant expression if and only if all required element-wise initializations for move and copy, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression.

3. Modify 22.4.4.2 [tuple.cnstr] as indicated:

   > -3- The defaulted move and copy constructors, respectively, of `tuple` is a constexpr functioncan be used in a constant expression if and only if all required element-wise initializations for move and copy, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression. The defaulted move and copy constructors of `tuple<>` are constexpr functionscan be used in a constant expression.

4. Modify 22.5.3.2 [optional.ctor] as indicated:

   >     constexpr optional() noexcept;
   >     constexpr optional(nullopt_t) noexcept;

   > > -1- […]

   > > -2- Remarks: No contained value is initialized. For every object type `T` these constructors are constexpr constructors (9.2.6 [dcl.constexpr]).

   > […]

   >     template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);

   > > -12- […]

   > > -13- […]

   > > -14- […]

   > > -15- […]

   > > -16- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > -17- […]

   > > -18- […]

   > > -19- […]

   > > -20- […]

   > > -21- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class U = T> constexpr explicit(see below) optional(U&& v);

   > > -22- […]

   > > -23- […]

   > > -24- […]

   > > -25- […]

   > > -26- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor. The expression inside `explicit` is equivalent to:

   > > >     !is_convertible_v<U, T>

5. Modify 22.5.3.7 [optional.observe] as indicated:

   >     constexpr const T* operator->() const;
   >     constexpr T* operator->();

   > > -1- […]

   > > -2- […]

   > > -3- […]

   > > -4- Remarks: These functions are constexpr functions.

   >     constexpr const T& operator*() const&;
   >     constexpr T& operator*() &;

   > > -5- […]

   > > -6- […]

   > > -7- […]

   > > -8- Remarks: These functions are constexpr functions.

   > […]

   >     constexpr explicit operator bool() const noexcept;

   > > -11- Returns: `true` if and only if `*this` contains a value.

   > > -12- Remarks: This function is a constexpr function.

   >     constexpr bool has_value() const noexcept;

   > > -13- Returns: `true` if and only if `*this` contains a value.

   > > -14- Remarks: This function is a constexpr function.

6. Modify 22.5.7 [optional.relops] as indicated:

   >     template<class T, class U> constexpr bool operator==(const optional<T>& x, const optional<U>& y);

   > > -1- […]

   > > -2- […]

   > > -3- Remarks: Specializations of this function template for which `*x == *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator!=(const optional<T>& x, const optional<U>& y);

   > > -4- […]

   > > -5- […]

   > > -6- Remarks: Specializations of this function template for which `*x != *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator<(const optional<T>& x, const optional<U>& y);

   > > -7- […]

   > > -8- […]

   > > -9- Remarks: Specializations of this function template for which `*x < *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator>(const optional<T>& x, const optional<U>& y);

   > > -10- […]

   > > -11- […]

   > > -12- Remarks: Specializations of this function template for which `*x > *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator<=(const optional<T>& x, const optional<U>& y);

   > > -13- […]

   > > -14- […]

   > > -15- Remarks: Specializations of this function template for which `*x <= *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator>=(const optional<T>& x, const optional<U>& y);

   > > -16- […]

   > > -17- […]

   > > -18- Remarks: Specializations of this function template for which `*x >= *y` is a core constant expression are constexpr functions.

   >     template<class T, three_way_comparable_with<T> U>
   >       constexpr compare_three_way_result_t<T,U>
   >         operator<=>(const optional<T>& x, const optional<U>& y);

   > > -19- Returns: If `x && y`, `*x <=> *y`; otherwise `bool(x) <=> bool(y)`.

   > > -20- Remarks: Specializations of this function template for which `*x <=> *y` is a core constant expression are constexpr functions.

7. Modify 22.6.3.2 [variant.ctor] as indicated:

   >     constexpr variant() noexcept(see below);

   > > -1- […]

   > > -2- […]

   > > -3- […]

   > > -4- […]

   > > -5- […]

   > > -6- Remarks: This function is `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a constexpr function. The expression inside `noexcept` is equivalent to `is_nothrow_default_constructible_v<T0>`. [Note: See also class `monostate`. — end note]

   > […]

   >     template<class T> constexpr variant(T&& t) noexcept(see below);

   > > -14- […]

   > > […]

   > > -19- Remarks: The expression inside `noexcept` is equivalent to `is_nothrow_constructible_v<Tj, T>`. If `Tj`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);

   > > -20- […]

   > > […]

   > > -24- Remarks: If `T`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class T, class U, class... Args>
   >       constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);

   > > -25- […]

   > > […]

   > > -29- Remarks: If `T`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);

   > > -30- […]

   > > […]

   > > -34- Remarks: If `TI`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<size_t I, class U, class... Args>
   >       constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);

   > > -35- […]

   > > […]

   > > -38- Remarks: If `TI`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

8. Modify 24.5.4.11 [move.sent.ops] as indicated:

   >     constexpr move_sentinel();

   > > -1- Effects: Value-initializes `last`. If `is_trivially_default_constructible_v<S>` is `true`, then this constructor is a constexpr constructor.

9. Modify 22.11.3 [bit.cast] as indicated:

   >     template<class To, class From>
   >       constexpr To bit_cast(const From& from) noexcept;

   > > -1- […]

   > > -3- Remarks: This function is `constexpr` can be used in a constant expression if and only if `To`, `From`, and the types of all subobjects of `To` and From are types `T` such that:

   > > 1. (3.1) — `is_union_v<T>` is `false`;

   > > 2. (3.2) — `is_pointer_v<T>` is `false`;

   > > 3. (3.3) — `is_member_pointer_v<T>` is `false`;

   > > 4. (3.4) — `is_volatile_v<T>` is `false`; and

   > > 5. (3.5) — `T` has no non-static data members of reference type.

10. Modify 30.5 [time.duration] as indicated:

    > -5- The defaulted copy constructors of duration shall be a constexpr functioncan be used in a constant expression if and only if the required initialization of the member `rep_` for copy and move, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression.

### 2844(i). Stability of `a_uniq.insert(i, j)`

Section: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: Open Submitter: Matt Austern Opened: 2016-12-14 Last modified: 2020-02-14

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Open status.

Discussion:

If we write `a_uniq.insert(i, j)` and `[i, j)` has multiple elements with keys that compare equivalent, which ones get inserted? Consider, for example, inserting into a `map<string, int>` with

>     m.insert({{"red", 5}, {"green", 3}, {"red", 7}, {"blue", 2}, {"pink", 6}});

Which value for `"red"` will the map have?

On my implementation we got `"red" -> 5`, and I suspect that's true on most or all implementations, but I don't believe that's guaranteed by anything in the requirements. The wording in Table 90 just says that it "inserts each element from the range `[i, j)` if and only if there is no element with key equivalent to the key of that element", but that doesn't tell us what happens if `[i, j)` contains duplicate keys because it doesn't say what order the insertions are performed in. The standard should either guarantee that the first value is the one that gets inserted, or explicitly say that this is unspecified.

The same issue applies to the range constructor, and to the unordered associative containers.

[2017-01-27 Telecon]

Priority 3; Nico to provide wording.

[2020-02-14, Prague]

LWG discussion. Suggestion to specify that we like the direction of the wording for `insert` of unordered containers, but would also like to clarify that the loop is meant to be "in order" of the sequence elements.

Daniel K. volunteered to provide such wording.

Proposed resolution:

### 2845(i). `enable_if`, `result_of`, `common_type` and `aligned_storage` do not meet the definition of `TransformationTrait`

Section: 21.3.2 [meta.rqmts] Status: New Submitter: Tim Song Opened: 2016-12-14 Last modified: 2017-02-02

Priority: 3

View all other issues in [meta.rqmts].

View all issues with New status.

Discussion:

[meta.rqmts]/3 defines `TransformationTrait` as follows:

> A `TransformationTrait` modifies a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the modification. It shall define a publicly accessible nested type named `type`, which shall be a synonym for the modified type.

`enable_if`, `result_of` and `common_type` do not necessarily "define a publicly accessible nested type named `type`". `aligned_storage` takes no template type argument (it only has two non-type parameters). Yet [meta.trans]/2 says that they are all `TransformationTraits`.

Incidentally, with the exception of `decay`, it's not clear that any of the traits in [meta.trans.other] could really be described as "modify[ing] a property of a type".

[2017-01-27 Telecon]

Priority 3

Proposed resolution:

### 2846(i). Undefined phrase "effectively cast"

Section: 29.4.10 [cmplx.over], 29.7.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2019-03-18

Priority: 3

View all other issues in [cmplx.over].

View all issues with New status.

Discussion:

In [cmplx.over] and [cmath.syn], when talking about "sufficient additional overloads", we use the phrase "effectively cast", but that is not a defined term.

A hostile interpretation could read "`reinterpret_cast`" here.

Likely we mean "apply floating-point promotions, floating-integral conversions, and floating-point conversions", but that should be spelled out somewhere, e.g. in the library definitions section.

(Source: Editorial issue #1248)

[2017-01-27 Telecon]

Priority 3

[2019-03-16; Daniel comments and provides wording]

I decided to use the form "implicitly converted" and to refer to 7.3 [conv] for 29.7.1 [cmath.syn] and 29.4.10 [cmplx.over], because those conversions can all be done implicitly. This also holds for the `pow` specification 29.4.10 [cmplx.over] p3, because the described conversions of `complex<T>` to `complex<U>` involve only the need of non-explicit constructors.

Proposed resolution:

This wording is relative to N4810.

1. Change 29.4.10 [cmplx.over], as indicated:

   > -2- The additional overloads shall be sufficient to ensure:

   > > 1. (2.1) — If the argument has type `long double`, then it is effectively castimplicitly converted (7.3 [conv]) to `complex<long double>`.

   > > 2. (2.2) — Otherwise, if the argument has type `double` or an integer type, then it is effectively castimplicitly converted to `complex<double>`.

   > > 3. (2.3) — Otherwise, if the argument has type `float`, then it is effectively castimplicitly converted to `complex<float>`.

   > -3 Function template `pow` shall have additional overloads sufficient to ensure, for a call with at least one argument of type `complex<T>`:

   > > 1. (3.1) — If either argument has type `complex<long double>` or type `long double`, then both arguments are effectively castimplicitly converted (7.3 [conv]) to `complex<long double>`.

   > > 2. (3.2) — Otherwise, if either argument has type `complex<double>`, `double`, or an integer type, then both arguments are effectively castimplicitly converted to `complex<double>`.

   > > 3. (3.3) — Otherwise, if either argument has type `complex<float>` or `float`, then both arguments are effectively castimplicitly converted to `complex<float>`.

2. Change 29.7.1 [cmath.syn], as indicated:

   > -2- For each set of overloaded functions within `<cmath>`, with the exception of `abs`, there shall be additional overloads sufficient to ensure:

   > > 1. If any argument of arithmetic type corresponding to a `double` parameter has type `long double`, then all arguments of arithmetic type (6.9.2 [basic.fundamental]) corresponding to `double` parameters are effectively castimplicitly converted (7.3 [conv]) to `long double`.

   > > 2. Otherwise, if any argument of arithmetic type corresponding to a `double` parameter has type `double` or an integer type, then all arguments of arithmetic type corresponding to `double` parameters are effectively castimplicitly converted to `double`.

   > > 3. Otherwise, all arguments of arithmetic type corresponding to `double` parameters have type `float`.

### 2847(i). `sin(float)` should call `sinf(float)`

Section: 29.7.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2020-09-06

Priority: 3

View other active issues in [cmath.syn].

View all other issues in [cmath.syn].

View all issues with New status.

Discussion:

With P0175R1, we now show in [cmath.syn] three overloads for the `sin` function: One taking a `float`, one taking a `double`, and one taking a `long double`. However, there is no statement that `sin(long double)` should actually invoke `sinl`, presumably delivering extra precision.

An implementation like

>     inline long double sin(long double x)
>     { return sinf(x); }

seems to satisfy the "effectively cast" requirement, but is certainly unintentional.

The same issue arises for all math functions inherited from C.

(Source: Editorial issue #1247)

[2017-01-27 Telecon]

Priority 3

Proposed resolution:

### 2848(i). Pass-through threshold for pool allocator

Section: 20.5.5.2 [mem.res.pool.options] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2017-02-02

Priority: 3

View all issues with New status.

Discussion:

20.5.5.2 [mem.res.pool.options] p3 talks about a "pass-through-threshold".

First, the phrase is not defined and it seems it could be easily avoided given the context.

Second, given the phrasing here, it seems the implementation is essentially allowed to ignore the value `largest_required_pool_block` as it sees fit. It is unclear whether that is the intention.

[2017-01-27 Telecon]

Priority 3; Jonathan will ask Alisdair for wording.

Proposed resolution:

### 2881(i). Adopt section III of P0308R0

Section: 22.6.3 [variant.variant] Status: New Submitter: Switzerland Opened: 2017-02-03 Last modified: 2020-09-06

Priority: 3

View all other issues in [variant.variant].

View all issues with New status.

Discussion:

Addresses CH 7

Consider making the variant statically `!valueless_by_exception()` for cases where `is_nothrow_move_constructible_v<T_i>` for all alternative types `Ti`

Proposed change: Adopt section III of P0308R0.

[2017-07 Toronto Thurs Issue Prioritization]

Priority 3. This is similar to 2904(i), Casey to investigate

Proposed resolution:

### 2883(i). The standard library should provide `string_view` parameters instead or in addition for functions defined with `char const *` or `string const &` as parameter types.

Section: 27.3 [string.view] Status: LEWG Submitter: Switzerland Opened: 2017-02-03 Last modified: 2025-10-21

Priority: 4

View other active issues in [string.view].

View all other issues in [string.view].

View all issues with LEWG status.

Discussion:

Addresses CH 9

The standard library should provide `string_view` parameters instead or in addition for functions defined with `char const *` or `string const &` as parameter types. Most notably in cases where both such overloads exist or where an internal copy is expected anyway.

It might be doubted that the non-null termination of `string_view` could be an issue with functions that pass the `char *` down to OS functions, such as `fstream_buf::open()` etc. and those shouldn't provide it and favour generating a `std::string` temporary instead in that case. However, `std::path` demonstrates it is usable to have `string_view` overloads and there might be many places where it can be handy, or even better.

Proposed change: Provide the overloads for `std::regex`, the exception classes, `std::bitset`, `std::locale` and more.

[Post-Kona 2017]

Most (all?) of these changes were proposed in P0506r1, which was discussed by LEWG in Kona.

[2017-07 Toronto Thurs Issue Prioritization]

Status LEWG - they're already looking at this.

[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]

Proposed resolution:

### 2906(i). There is no ability to supply an allocator for the control block when constructing a `shared_ptr` from a `unique_ptr`

Section: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-15

Priority: 3

View other active issues in [util.smartptr.shared.const].

View all other issues in [util.smartptr.shared.const].

View all issues with New status.

Discussion:

Addresses US 130

There is no ability to supply an allocator for the control block when constructing a `shared_ptr` from a `unique_ptr`. Note that no further `shared_ptr` constructors need an allocator, as they all have pre-existing control blocks that are shared, or already have the allocator overload.

Proposed change: Add an additional `shared_ptr` constructor, `template <class Y, class D, class A> shared_ptr(unique_ptr<Y, D>&& r, A alloc)`, with the same semantics as the existing constructor taking a `unique_ptr`, but using the `alloc` argument to supply memory as required.

[2017-07 Toronto Thurs Issue Prioritization]

Priority 3; Alisdair to provide wording

Proposed resolution:

### 2923(i). `noexcept` is inconsistently applied across headers which import components of the C standard library

Section: 29.7.1 [cmath.syn] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-15

Priority: 4

View other active issues in [cmath.syn].

View all other issues in [cmath.syn].

View all issues with New status.

Discussion:

Addresses US 172

`noexcept` is inconsistently applied across headers which import components of the C standard library into the C++ library; some functions (`std::abort()`, `std::_Exit()`, etc) are defined as `noexcept` in some places, but not in others. Some functions which seem like they should be `noexcept` (`std::abs()`, `std::div()`, etc) are not defined as `noexcept`.

Proposed change: Make the majority of the C library functions (with exceptions such as `std::qsort()` and `std::bsearch()`, which can call user code) `noexcept`.

[2017-07 Toronto Thurs Issue Prioritization]

Priority 4

Proposed resolution:

### 2931(i). Missed optimization opportunity with single-argument `std::next`

Section: 24.4.3 [iterator.operations] Status: Open Submitter: Morwenn Opened: 2017-02-04 Last modified: 2018-12-03

Priority: 3

View other active issues in [iterator.operations].

View all other issues in [iterator.operations].

View all issues with Open status.

Discussion:

It seems that `std::next` is missing an optimization opportunity when taking a single parameter. The standard mandates that `std::next` shall call `std::advance` on the passed iterator and return it. For random-access iterators, it means that `operator+=` will be called on the iterator. However, if a single-argument overload was added to `std::next`, it could call `++it` directly instead of `std::advance(it, 1)`, which means that `operator++` would be called instead of `operator+=`. This might make a small performance difference for complicated iterators such as `std::deque`'s ones, where `operator++` has a simpler logic than `operator+=`.

An equivalent optimization could be allowed by adding a single-argument overload to `std::prev` too.

[2017-03-04, Kona]

Set priority to 3. Alisdair to provide wording.

[2018-11-30, Jonathan comments, recommending NAD]

Jonathan suggested NAD, because the proposed "just use increment when `n==1`" optimization can be done in `std::next` (and/or `std::advance`, and/or complicated iterators like `deque::iterator`) without adding an overload. Billy said the overload would avoid metaprogramming costs for dispatching to the right `std::advance`, and help in non-optimized builds. Zhihao said the overload would make it clear to users that the `n==1` case is optimized (Jonathan thinks this is irrelevant as there's no requirement that we tell users what we optimize).

Proposed resolution:

### 2939(i). Some type-completeness constraints of traits are overspecified

Section: 21.3.3 [meta.type.synop] Status: Open Submitter: Daniel Krügler Opened: 2017-03-02 Last modified: 2024-08-21

Priority: 2

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with Open status.

Discussion:

LWG 2797(i) (RU 2) suggests that certain type-traits should be required to diagnose violations of their pre-conditions. The basic idea is founded and I see no problems for requiring this for the mentioned traits `alignment_of` or `is_base_of`, for example. But if we want to require this diagnostics for some other traits, such as `is_convertible`, `is_constructible` (and friends), or `is_callable` (and possibly some others), we really should be sure that our current requirements are OK.

Unfortunately, there exists some cases, where we currently overspecify imposing complete type requirements where they are not actually required. For example, for the following situation the answer of the trait could be given without ever needing the complete type of `X`:

>     struct X; // Never defined

>     static_assert(std::is_convertible_v<X, const X&>);

Unfortunately we cannot always allow incomplete types, because most type constructions or conversions indeed require a complete type, so generally relaxing the current restrictions is also not an option.

The core language has a solution for this "small" gap of situations, where the response of the compiler might depend on type completeness: Undefined behaviour. So, I believe we need a somewhat more detailled form to express the intend here. Informally, I would suggest that the program should only be ill-formed in the situation described by LWG 2797(i), if there exists the possibility that the compiler would require complete types for the considered operation. The example shown above, `std::is_convertible_v<X, const X&>`, would never require the need to complete `X`, so here no violation should exist.

The presented example might seem a tiny one, but the Standard Library type traits are extreme fundamental tools and we should try to not give the impression that an approximate rule of the current type constraints breaks reasonable code.

It is correct, that above example has currently undefined behaviour due to the breakage of pre-conditions, therefore this issue suggests to fix the current situation before enforcing a diagnostic for such valid situations.

[2017-03-04, Kona]

Set priority to 2. Is related to 2797(i), but really needs an audit of the type traits.

[2018-08 Batavia Monday issue discussion]

Issues 2797(i), 2939(i), 3022(i), and 3099(i) are all closely related. Walter to write a paper resolving them.

[2020-02 Prague Thursday issue discussion]

Two of the issues (2797(i) and 3022(i)) had been resolved by the acceptance of P1285R0.

[2024-05-09; Jonathan provides wording]

We could also relax the type completeness requirements for `reference_converts_from_temporary` and `reference_constructs_from_temporary`, as the result is always false if the first type is a non-reference, so we don't need complete types in that case. This doesn't seem important to support, but if we wanted to then we could say:

> Either `T` is not a reference type, or `T` and `U` shall be a complete type complete types, cv `void`, or an arrayarrays of unknown bound.

Previous resolution [SUPERSEDED]

This wording is relative to N4981.

> 1. In 21.3.5.4 [meta.unary.prop] Table 51, change the Preconditions text for `is_constructible`, `is_trivially_constructible`, `is_nothrow_constructible`, `is_convertible`, and `is_nothrow_convertible`, as indicated.

>    > Template | Condition | Preconditions
>    > --- | --- | ---
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_constructible;
>    > | For a function type `T` or for a cv `void` type `T`, `is_constructible_v<T, Args...>` is `false`, otherwise see below . | Either `T` is a reference type and `Args` contains a single type that is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_trivially_constructible;
>    > | `is_constructible_v<T, Args...>` is `true` and the variable definition for `is_constructible`, as defined below, is known to call no operation that is not trivial (6.9.1 [basic.types.general], 11.4.4 [special]). | Either `T` is a reference type and `Args` contains a single type that is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_nothrow_constructible;
>    > | `is_constructible_v<T, Args...>` is `true` and the variable definition for `is_constructible`, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). | Either `T` is a reference type and `Args` contains a single type that is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …

> 2. In 21.3.7 [meta.rel] Table 53, change the Comments text for `is_convertible` and `is_nothrow_convertible` as indicated.

>    > Template | Condition | Comments
>    > --- | --- | ---
>    > … | … | …
>    >     template<class From, class To>
>    >     struct is_convertible;
>    > | see below | Either `To` is a reference type and `From` is similar (7.3.6 [conv.qual]) to `remove_reference_t<To>`, or `From` and `To` shall be complete types, cv `void`, or arrays of unknown bound.
>    >     template<class From, class To>
>    >     struct is_nothrow_convertible;
>    > | `is_convertible_v<From, To>` is `true` and the conversion, as defined by `is_convertible`, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). | Either `To` is a reference type and `From` is similar (7.3.6 [conv.qual]) to `remove_reference_t<To>`, or `From` and `To` shall be complete types, cv `void`, or arrays of unknown bound.

[2024-08-21; Jonathan provides improved wording]

Following on LWG telecon review, change "Args contains a single type that is similar" to "Args contains a single type and that type is similar".

Proposed resolution:

This wording is relative to N4988.

> 1. In 21.3.5.4 [meta.unary.prop] Table 51, change the Preconditions text for `is_constructible`, `is_trivially_constructible`, `is_nothrow_constructible`, `is_convertible`, and `is_nothrow_convertible`, as indicated.

>    > Template | Condition | Preconditions
>    > --- | --- | ---
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_constructible;
>    > | For a function type `T` or for a cv `void` type `T`, `is_constructible_v<T, Args...>` is `false`, otherwise see below . | Either `T` is a reference type and `Args` contains a single type and that type is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_trivially_constructible;
>    > | `is_constructible_v<T, Args...>` is `true` and the variable definition for `is_constructible`, as defined below, is known to call no operation that is not trivial (6.9.1 [basic.types.general], 11.4.4 [special]). | Either `T` is a reference type and `Args` contains a single type and that type is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …
>    >     template<class T, class... Args>
>    >     struct is_nothrow_constructible;
>    > | `is_constructible_v<T, Args...>` is `true` and the variable definition for `is_constructible`, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). | Either `T` is a reference type and `Args` contains a single type and that type is similar (7.3.6 [conv.qual]) to `remove_reference_t<T>`, or `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
>    > … | … | …

> 2. In 21.3.7 [meta.rel] Table 53, change the Comments text for `is_convertible` and `is_nothrow_convertible` as indicated.

>    > Template | Condition | Comments
>    > --- | --- | ---
>    > … | … | …
>    >     template<class From, class To>
>    >     struct is_convertible;
>    > | see below | Either `To` is a reference type and `From` is similar (7.3.6 [conv.qual]) to `remove_reference_t<To>`, or `From` and `To` shall be complete types, cv `void`, or arrays of unknown bound.
>    >     template<class From, class To>
>    >     struct is_nothrow_convertible;
>    > | `is_convertible_v<From, To>` is `true` and the conversion, as defined by `is_convertible`, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). | Either `To` is a reference type and `From` is similar (7.3.6 [conv.qual]) to `remove_reference_t<To>`, or `From` and `To` shall be complete types, cv `void`, or arrays of unknown bound.

### 2947(i). Clarify several filesystem terms

Section: 31.12.8.1 [fs.enum.path.format], 31.12.11 [fs.class.directory.iterator], 31.12.13.4 [fs.op.copy] Status: New Submitter: Thomas Köppe Opened: 2017-03-14 Last modified: 2023-02-07

Priority: 3

View all issues with New status.

Discussion:

During the application of P0430R2, we came across several terms that seem insufficiently clear and lacking proper definitions.

We would like clarification on what those terms mean, and we would welcome wording suggestions, or alternatively a verbose explanation and dispensation to change the presentation editorially.

The items in question are:

- What does it mean to 'always interpret [the character sequence] in the same way'? ([fs.enum.path.format], which is [path.format] in the paper)

- What is a "directory-like" file type? ([fs.class.directory_iterator])

- What is an "implementation-defined file type"? (e.g. [fs.op.copy])

[2017-07 Toronto Wed Issue Prioritization]

Priority 3

[2018-01-16, Jonathan comments]

In 31.12.8.1 [fs.enum.path.format] "always interpreted in the same way" means irrespective of the `path::format` value, or the content of the string. Maybe add ", rather than depending on the `path::format` value or the content of the character sequence".

In [fs.class.directory_iterator] an "implementation-defined directory-like file type" is an implementation-defined file type (see [fs.enum.file_type] and Table 115) that is treated like a directory by the special rules that the OS has for non-regular files (see 31.12.6.2 [fs.path.generic]).

In 31.12.13.4 [fs.op.copy], an "implementation-defined file type" is exactly that, see [fs.enum.file_type] and Table 115 again. I don't see what isn't clear about that. Maybe add a cross-reference to [fs.enum.file_type].

Proposed resolution:

### 2949(i). Unclear complexity requirements: space vs. time

Section: 16 [library] Status: New Submitter: Jens Maurer Opened: 2017-03-20 Last modified: 2017-07-15

Priority: 4

View other active issues in [library].

View all other issues in [library].

View all issues with New status.

Discussion:

This is from editorial issue #1088:

It is not always made explicit whether the requirement is referring to time or space complexity, or both.

> "Linear time." vs. "Linear."
> "Constant time." vs. "Constant."

16.3.2.4 [structure.specifications] says that the Complexity element specifies "the time and/or space complexity of the function", so being explicit about this would be good.

Examples:

- [sequence.reqmts] `a.clear()`

- [deque.cons]

- [vector.cons]

- [map.cons]

- [multimap.cons]

- [set.cons]

- [multiset.cons]

- [unord.map.cnstr]

- [unord.multimap.cnstr]

etc.

[2017-07 Toronto Wed Issue Prioritization]

Priority 4; Robert to look at

Proposed resolution:

### 2959(i). `char_traits<char16_t>::eof` is a valid UTF-16 code unit

Section: 27.2.4.4 [char.traits.specializations.char16.t] Status: New Submitter: Jonathan Wakely Opened: 2017-05-05 Last modified: 2019-04-02

Priority: 3

View all other issues in [char.traits.specializations.char16.t].

View all issues with New status.

Discussion:

The standard requires that `char_traits<char16_t>::int_type` is `uint_least16_t`, so when that has the same representation as `char16_t` there are no bits left to represent the `eof` value.

27.2.4.4 [char.traits.specializations.char16.t] says:

> — The member `eof()` shall return an implementation-defined constant that cannot appear as a valid UTF-16 code unit.

Existing practice is to use the "noncharacter" `u'\uffff'` for this value, but the Unicode spec is clear that `U+FFFF` and other noncharacters are valid, and their appearance in a UTF-16 string does not make it ill-formed. See here and here:

> The fact that they are called "noncharacters" and are not intended for open interchange does not mean that they are somehow illegal or invalid code points which make strings containing them invalid.

In practice this means there's no way to tell if `basic_streambuf<char16_t>::sputc(u'\uffff')` succeeded or not. If it can insert the character it returns `to_int_type(u'\uffff')` and otherwise it returns `eof()`, which is the same value.

I believe that `char_traits<char16_t>::to_int_type(char_type c)` can be defined to transform `U+FFFF` into `U+FFFD`, so that the invariant `eq_int_type(eof(), to_int_type(c)) == false` holds for any `c` (and the return value of `sputc` will be distinct from `eof`). I don't think any implementation currently meets that invariant.

I think at the very least we need to correct the statement "The member `eof()` shall return an implementation-defined constant that cannot appear as a valid UTF-16 code unit", because there are no such constants if `sizeof(uint_least16_t) == sizeof(char16_t)`.

This issue is closely related to LWG 1200(i), but there it's a slightly different statement of the problem, and neither the submitter's recommendation nor the proposed resolution solves this issue here. It seems that was closed as NAD before the Unicode corrigendum existed, so at the time our standard just gave "surprising results" but wasn't strictly wrong. Now it makes a normative statement that conflicts with Unicode.

[2017-07 Toronto Wed Issue Prioritization]

Priority 3

Proposed resolution:

### 2962(i). Iterators of Containers of move-only types do not model `InputIterator`

Section: 24.3.5.3 [input.iterators] Status: Open Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2025-10-15

Priority: 2

View other active issues in [input.iterators].

View all other issues in [input.iterators].

View all issues with Open status.

Discussion:

In Table 95 in 24.3.5.3 [input.iterators], it is specified that the expression `*a` returns `reference`, which must be convertible to `value_type`. This is not true for move-only types, which incidentally means that `std::vector<std::unique_ptr<int>>` does not possess even a lowly `InputIterator`, which is, of course, absurd.

With the advent of concepts as first-class citizens in the language, getting this right as soon as possible is a priority.

This issue seems to be similar to both LWG 448(i) and LWG 484(i), but not the same.

The proposed resolution stems from two considerations outlined below:

> Convertibility is too strong for all algorithms

> No algorithm in the standard library requires convertibility to `value_type`. If algorithms require things that smell of that, they specify the assignment or constructibility flavor they need directly. I checked this by going through the specification of each and every one of them in `<algorithm>` and `<numeric>`, which highlighted several issues unrelated to this one. These issues are presented in Algorithms with underspecified iterator requirements (LWG 2963(i)).

> `reference` needs to be related to `value_type`

> Algorithms need this for the following reasons:

> - lifetime-extension: served as adequately by `T const&` as by `T`. Also works for iterators that return by value. `T&&` also correctly binds to `T const&`.

> - passing to predicates: again, served adequately by `T const&`

> - writing to `*result`: not provided by the requirement anyway.

> - capture-by-copy: currently implicitly guaranteed, but unused in the standard library (always specified separately). A separate specification can always be made for algorithms that need to capture-by-copy.

> We must give due consideration to code that so far required its inputs to be `CopyConstructible` implicitly by requiring convertibility to `T`. This is done in the issue LWG 2963(i), which presents the results of a comb-through of `<algorithm>` and `<numeric>` to find algorithms that have this requirement, but where it is not specified. While related issues have been identified, no algorithms seems to require more than `T const&` convertibility without separately requiring convertibility to `T`.

> Since such code is already compiling today, relaxing this requirement does not break code.

> The only code this could possibly break is if, in a concept checking library, the `InputIterator` concept requirement on `reference` being convertible to `value_type` gets relaxed. Such a library, if it offered overloading based on most-specific modeled concept, could now, once fixed, resolve the call to a different algorithm, which could break user code that uses a hypothetical algorithm with a move-only container and was relying to select some other overload for move-only types based on the implicit `CopyConstructible` assertion provided by the iterator.

> In our internal concepts-checking library, we have had this issue "fixed" since the very beginning — move-only types were too important for our internal algorithms library, and also no algorithm in it seems to require something like `Iterator::value_type x = *first` without also requiring copy-constructibility anyway.

[2017-07 Toronto Monday issue prioritization]

Priority 2; also could affect the ranges TS

Previous resolution [SUPERSEDED]:

> This wording is relative to N4659.

> 1. Change Table 95 — "Input iterator requirements", 24.3.5.3 [input.iterators] as indicated:

>    > Table 107 — Input iterator requirements (in addition to Iterator)
>    > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
>    > --- | --- | --- | ---
>    > `…`
>    > `*a` | `reference`, convertible to `T` that binds to `const T&` | `` | […]
>    > `…`
>    > `*r++` | convertible to `T` that binds to `const T&` | `{ Tauto&& tmp = *r; ++r; return tmp; }` |

[2018-04-20; Eric Niebler provides improved wording]

The revised wording makes it clear that you can only rely on those operational semantics when the value type is constructible from the reference type and is movable. When those conditions aren't met, we can make no guarantees about the operational semantics of `*r++` (which is why `*r++` is no longer a required expression of the `InputIterator` concept in the Ranges TS).

Really, no generic code should be doing `*r++` on input iterators. Another option would be to simply deprecate this requirement for input iterators, but that might need a paper. (For forward iterators, `*r++` is already required to return `reference` exactly, and the multi-pass guarantee gives it the proper semantics.)

I also now have a question about the proposed return type of `*a` and `*r++`, which says they must be something that "binds to `const T&`". Does this mean that an iterator with a reference type reference-to-[`const`?]-`volatile`-`T` is no longer considered an iterator? I don't think that's what we want to say. Perhaps these should read "binds to `const volatile T&` instead, except that has the problem for InputIterators that return prvalues that a prvalue is not bindable to a volatile reference.

[2018-11 San Diego Thursday night issue processing]

Look at Ranges; EricWF to investigate. Status to Open

[2025-10-15; additional suggestion from Eric Niebler during 2018-04 discussion]

Regarding the issue of whether `int volatile*` is an iterator or not, I think the best way to solve the problem is with the `common_reference_t` type trait. The return type for `*a` could be "`reference`, where `common_reference_t<reference&&, T&>` is well-formed" and for `*r++` could be: "A type `U`, where `common_reference_t<U&&, T&>` is well-formed".

Previous resolution [SUPERSEDED]:

> This wording is relative to N4741.

> 1. Change Table 89 — "Input iterator requirements", 24.3.5.3 [input.iterators] as indicated:

>    > Table 89 — Input iterator requirements (in addition to Iterator)
>    > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
>    > --- | --- | --- | ---
>    > `…`
>    > `*a` | `reference`, convertible to `T` that binds to `const T&` | `` | […]
>    > `…`
>    > `*r++` | convertible to `T` that binds to `const T&` | When `T tmp = *r` is well-formed and `T` is `MoveConstructible`, then `{ T tmp = *r; ++r; return tmp; }` |

[2022-04-25; Daniel rebases wording on N4910]

Proposed resolution:

This wording is relative to N4910.

1. Change 24.3.5.3 [input.iterators], Table 83 — "Cpp17InputIterator requirements (in addition to Cpp17Iterator) [tab:inputiterator]" as indicated:

   > Table 83 — Cpp17InputIterator requirements (in addition to Cpp17Iterator) [tab:inputiterator]
   > Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
   > --- | --- | --- | ---
   > `…`
   > `*a` | `reference`, convertible to `T` that binds to `const T&` | `` | […]
   > `…`
   > `*r++` | convertible to `T` that binds to `const T&` | When `T tmp = *r` is well-formed and `T` is Cpp17MoveConstructible, then `{ T tmp = *r; ++r; return tmp; }` |

### 2963(i). Algorithms with underspecified iterator requirements

Section: 26 [algorithms], 29 [numerics] Status: New Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2017-07-12

Priority: 3

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with New status.

Discussion:

While researching whether the proposed resolution of Iterators of Containers of move-only types do not model `InputIterator` (LWG 2962(i)), I came across several algorithms that underspecify their requirements, mostly with regard to some associated type of the iterator type they operate on. A list can be found below.

The list of algorithms with underspecified requirements from `<algorithm>` and `<numeric>` follows.

With the advent of concepts, these algorithms will need better specifications if we are ever hoping to be allowed to overload based on them. I want this issue to bring the standard algorithms closer to having their concept requirements directly transcribable to library annotations.

Suggested resolution:

1. `copy`, `copy_if`, `copy_n`, `copy_backward`

   Add to description: `*result` shall be assignable from `*first`.

2. `move`, `move_backward`

   Add to description: `*result` shall be move-assignable from `*first`.

3. `transform`

   Add to description: The result of the expression `op(*first)` or `binary_op(*first1, *first2)` shall be writable to `result`.

4. `rotate_copy`

   Add to description: `*first` shall be writable to `result`.

5. `merge`

   Add to description: `*first1` and `*first2` shall be writable to `result`..

6. `set_union`, `set_intersection`, `set_difference`, `set_symmetric_difference`

   Add to description: `*first1` and `*first2` shall be writable to `result`.

7. `partial_sum`

   `acc` is not defined.

   Change description: `acc`, a variable of `InputIterator`'s value type, shall be constructible

8. `adjacent_difference`

   `acc` is not defined.

   Change description: `acc`, a variable of `InputIterator`'s value type, shall be `MoveAssignable` and shall be `constructible` from the type of `*first`.

9. `iota`

   `iota` is mis-specified. Since the expression we need to support is `*first = value`: `*first` is required to be of type `InputIterator::reference`, and value is an lvalue of type `T`. The current specification allows calling `iota` with a `const` output iterator!

   `T` shall be convertible to `ForwardIterator`'s value type `value` shall be writable to `first`. The expression `*first = value` shall not modify `value`.

[2017-07 Toronto Monday issue prioritization]

Priority 3; Marshall to work with Gaspar to improve wording.

Proposed resolution:

### 2973(i). `inplace_merge` exact comparison count complexity prohibits useful real-world optimizations

Section: 26.8.6 [alg.merge] Status: LEWG Submitter: Billy Robert O'Neal III Opened: 2017-06-08 Last modified: 2026-06-08

Priority: 4

View all other issues in [alg.merge].

View all issues with LEWG status.

Discussion:

At the moment, `inplace_merge` requires exactly `N - 1` comparisons, if enough additional memory is available (and in practice enough additional memory is always available). However, this prohibits implementing the merge operation using forms of binary search, as in Timsort's 'Galloping Mode', a useful optimization for non-uniform input data. It's not really useful to prohibit standard libraries from trying a few extra speculative compares like this, given that users must be prepared for the fallback "not enough memory" 𝒪(`N lg N`) algorithm.

[2017-07 Toronto Monday issue prioritization]

Status to LEWG

[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]

Proposed resolution:

This wording is relative to N4659.

1. Edit 26.8.6 [alg.merge] as indicated:

   >     template<class BidirectionalIterator>
   >     void inplace_merge(BidirectionalIterator first,
   >         BidirectionalIterator middle,
   >         BidirectionalIterator last);
   >     template<class ExecutionPolicy, class BidirectionalIterator>
   >     void inplace_merge(ExecutionPolicy&& exec,
   >         BidirectionalIterator first,
   >         BidirectionalIterator middle,
   >         BidirectionalIterator last);
   >     template<class BidirectionalIterator, class Compare>
   >     void inplace_merge(BidirectionalIterator first,
   >         BidirectionalIterator middle,
   >         BidirectionalIterator last, Compare comp);
   >     template<class ExecutionPolicy, class BidirectionalIterator, class Compare>
   >     void inplace_merge(ExecutionPolicy&& exec,
   >         BidirectionalIterator first,
   >         BidirectionalIterator middle,
   >         BidirectionalIterator last, Compare comp);

   > > […]

   > > -8- Complexity: Let `N = last - first`:

   > > 1. (8.1) — For the overloads with no `ExecutionPolicy`, if enough additional memory is available, exactly `N - 1` comparisons on average, 𝒪(`N`) comparisons in the worst case.

   > > 2. (8.2) — For the overloads with no `ExecutionPolicy` if no additional memory is available, 𝒪(`N log N`) comparisons.

   > > 3. (8.3) — For the overloads with an `ExecutionPolicy`, 𝒪(`N log N`) comparisons.

   > > -9- Remarks: Stable (16.4.6.8 [algorithm.stable]).

### 2983(i). `money_put::do_put` underspecified

Section: 28.3.4.7.3.3 [locale.money.put.virtuals] Status: Ready Submitter: Jonathan Wakely Opened: 2017-06-21 Last modified: 2026-06-10

Priority: 3

View all other issues in [locale.money.put.virtuals].

View all issues with Ready status.

Discussion:

Whether you get "`.99`" or "`0.99`" for the following depends on the implementation:

>     std::cout.imbue(std::locale("en_US"));
>     std::cout << std::put_money(99.L);

I don't see any justification in [locale.money.put.virtuals] for the leading `0`, although that seems more useful.

If we want the leading zero, we should say so.

[2017-06-27, Jonathan comments and provides wording]

I suggest that we require a leading zero. The wording below is similar to how C specifies the `%f` format specifier for `fprintf`.

[Brno 2026-06-10; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N4659.

1. Edit 28.3.4.7.3.3 [locale.money.put.virtuals] as indicated:

   >     iter_type do_put(iter_type s, bool intl, ios_base& str,
   >                      char_type fill, long double units) const;
   >     iter_type do_put(iter_type s, bool intl, ios_base& str,
   >                      char_type fill, const string_type& digits) const;

   > > […]

   > > -2- Remarks: The currency symbol is generated if and only if `(str.flags() & str.showbase)` is nonzero. If the format specifies a decimal point, at least one digit character appears before it. If the number of characters generated for the specified format is less than the value returned by `str.width()` on entry to the function, then copies of `fill` are inserted as necessary to pad to the specified width. For the value `af` equal to `(str.flags() & str.adjustfield)`, if `(af == str.internal)` is `true`, the fill characters are placed where `none` or `space` appears in the formatting pattern; otherwise if `(af == str.left)` is `true`, they are placed after the other characters; otherwise, they are placed before the other characters.

### 2984(i). `put_money(99)` is unnecessarily undefined

Section: 31.7.8 [ext.manip] Status: New Submitter: Jonathan Wakely Opened: 2017-06-22 Last modified: 2017-06-26

Priority: 3

View all other issues in [ext.manip].

View all issues with New status.

Discussion:

[ext.manip] p5 says:

> Requires: The type `moneyT` shall be either `long double` or a specialization of the `basic_string` template (Clause 24).

This means that `put_money(99)`, `put_money(99.)`, `put_money("99")`, and `put_money(string_view{"99"})` are all undefined, when in practice they will compile fine and do the right thing, converting the argument to `long double` or `std::string` as needed.

We could change it to be "otherwise the program is ill-formed", or to remove the function templates from overload resolution when the argument is not `long double` or a `std::basic_string`, but that will unnecessarily break code that works fine today. We should accept types convertible to `long double` or the relevant `money_put` facet's `string_type` (which is not known until we attempt to write the unspecified type to an ostream).

The requirement is also insufficient, because `cout << put_money(wstring(L"99"))` won't compile on any implementation, despite the argument type being a specialization of `basic_string`. This same problem exists for `std::get_money`.

[2017-06-24, Daniel comments and provides wording]

The wording changes below are supposed to support all `moneyT` types that are convertible to either `long double` or to `money_put/get<Ch, o/istreambuf_iterator<Ch, Tr>>::string_type` (but not to both), where `Ch` and `Tr` are determined by the concrete instantiated specialization of the exposition-only function template `f` that is used to specify the semantics of `put_money` and `get_money`, respectively. XOR-ing the requirements outlaws types that are convertible to both, which would cause an ambiguity unless we would provide wording that would introduce an ordered application of these convertibility constraints. This is the rationale for the seemingly odd new Remarks formulation. Note also, that the wording provided below intentionally attempts to distinguish between the statically testable conditions based on the `is_convertible_v` expressions within the Remarks: element and the well-defined runtime behaviour requirement of the actually provided argument of deduced type `moneyT` within the pre-existing Requires: element. Another point worth pointing out is that the wording attempts to fix an currently existing ambiguity of the meaning of the type `moneyT` (and to a lesser extend for `charT` and `traits`) as either the template parameter of `put/get_money` or that of the corresponding template argument of the exposition-only `f` templates. The revised form makes it clearer that it refers to the latter.

It should be emphasized that this extension of the current wording would provide support for `put_money(99)`, `put_money(99.)`, and `put_money("99")`, but not yet for `put_money(string_view{"99"})`, because `string_view` is not convertible to `string`. To realize support for the latter, this wording approach could be extended by referring to `is_constructible` instead of `is_convertible`, though.

Proposed resolution:

This wording is relative to N4659.

1. Edit 31.7.8 [ext.manip] as indicated:

   >     template <class moneyT> unspecified get_money(moneyT& mon, bool intl = false);

   > > -?- For an expression `in >> get_money(mon, intl)` described below, let `Mo`, `Ch`, and `Tr` be the deduced template argument types of the template parameters `moneyT`, `charT`, and `traits`, respectively, of the instantiated specialization of the template `f`.

   > > -2- Requires: The type `moneyT` shall be either `long double` or a specialization of the `basic_string` template (Clause 27 [strings])`Mo` shall be either convertible to `long double` or shall be convertible to `money_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type`.

   > > -?- Remarks: If `is_convertible_v<Mo, long double> == is_convertible_v<Mo, money_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type>`, the program is ill-formed.

   > > -3- Effects: The expression `in >> get_money(mon, intl)` described below behaves as a formatted input function (31.7.5.3.1 [istream.formatted.reqmts]).

   > > -4- Returns: An object of unspecified type such that if `in` is an object of type `basic_istream<charTCh, traitsTr>` then the expression `in >> get_money(mon, intl)` behaves as if it called `f(in, mon, intl)`, where the function `f` is defined as:

   > > >     template <class charT, class traits, class moneyT>
   > > >     void f(basic_ios<charT, traits>& str, moneyT& mon, bool intl) {
   > > >       using Iter = istreambuf_iterator<charT, traits>;
   > > >       using MoneyGet = money_get<charT, Iter>;
   > > >       ios_base::iostate err = ios_base::goodbit;
   > > >       const MoneyGet& mg = use_facet<MoneyGet>(str.getloc());
   > > >       mg.get(Iter(str.rdbuf()), Iter(), intl, str, err, mon);
   > > >       if (ios_base::goodbit != err)
   > > >         str.setstate(err);
   > > >     }

   > > The expression `in >> get_money(mon, intl)` shall have type `basic_istream<charTCh, traitsTr>&` and value `in`.

   >     template <class moneyT> unspecified put_money(const moneyT& mon, bool intl = false);

   > > -?- For an expression `out << put_money(mon, intl)` described below, let `Mo`, `Ch`, and `Tr` be the deduced template argument types of the template parameters `moneyT`, `charT`, and `traits`, respectively, of the instantiated specialization of the template `f`.

   > > -5- Requires: The type `moneyT` shall be either `long double` or a specialization of the `basic_string` template (Clause 27 [strings])`Mo` shall be either convertible to `long double` or shall be convertible to `money_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type`.

   > > -?- Remarks: If `is_convertible_v<Mo, long double> == is_convertible_v<Mo, money_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type>`, the program is ill-formed.

   > > -6- Returns: An object of unspecified type such that if `out` is an object of type `basic_ostream<charTCh, traitsTr>` then the expression `out << put_money(mon, intl)` behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) that calls `f(out, mon, intl)`, where the function `f` is defined as:

   > > >     template <class charT, class traits, class moneyT>
   > > >     void f(basic_ios<charT, traits>& str, const moneyT& mon, bool intl) {
   > > >       using Iter = ostreambuf_iterator<charT, traits>;
   > > >       using MoneyPut = money_put<charT, Iter>;
   > > >       const MoneyPut& mp = use_facet<MoneyPut>(str.getloc());
   > > >       const Iter end = mp.put(Iter(str.rdbuf()), intl, str, str.fill(), mon);
   > > >       if (end.failed())
   > > >         str.setstate(ios::badbit);
   > > >     }

   > > The expression `out << put_money(mon, intl)` shall have type `basic_ostream<charTCh, traitsTr>&` and value `out`.

### 2986(i). Handling of multi-character collating elements by the `regex` FSM is underspecified

Section: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-12

Priority: 4

View other active issues in [re.grammar].

View all other issues in [re.grammar].

View all issues with New status.

Discussion:

In N4660 subclause 31.13 [re.grammar] paragraph 5:

> The productions `ClassAtomExClass`, `ClassAtomCollatingElement` and `ClassAtomEquivalence` provide functionality equivalent to that of the same features in regular expressions in POSIX.

The broadness of the above statement makes it sound like it is merely a statement of intent; however, this appears to be a necessary normative statement insofar as identifying the general semantics to be associated with the syntactic forms identified. In any case, if it is meant for `ClassAtomCollatingElement` to provide functionality equivalent to a collating symbol in a POSIX bracket expression, multi-character collating elements need to be considered.

In [re.grammar] paragraph 14:

> The behavior of the internal finite state machine representation when used to match a sequence of characters is as described in ECMA-262. The behavior is modified according to any `match_flag_type` flags specified when using the regular expression object in one of the regular expression algorithms. The behavior is also localized by interaction with the traits class template parameter as follows: [bullets 14.1 to 14.4]

In none of the bullets does the wording handle multi-character collating elements in a clear manner:

- 14.1 deals in characters.

- 14.2 deals in characters (`traits_inst.translate` accepts only a single character).

- 14.3 might handle a multi-character collating element; however, there is no specification of how such a collating element is to be identified from the sequence of characters. Additionally, the definition of primary equivalence class specifies that it is a set of characters (not of collating elements).

- 14.4 deals in characters.

The ECMA-262 specification for ClassRanges also deals in characters.

[2017-07 Toronto Monday issue prioritization]

Priority 4

Proposed resolution:

### 2987(i). Relationship between `traits_inst.lookup_collatename` and the `regex` FSM is underspecified with regards to `ClassAtomCollatingElement`

Section: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-12

Priority: 3

View other active issues in [re.grammar].

View all other issues in [re.grammar].

View all issues with New status.

Discussion:

For a user to implement a regular expression traits class meaningfully, the relationship between the return value of `traits_inst.lookup_collatename` to the behaviour of the finite state machine corresponding to a regular expression needs to be better specified.

From N4660 subclause 31.13 [re.grammar], `traits_inst.lookup_collatename` only feeds clearly into two operations:

1. a test if the returned string is empty ([re.grammar]/8), and

2. a test if the result of `traits_inst.transform_primary`, with the returned string, is empty ([re.grammar]/10).

Note: It is unclear if bullet 14.3 in [re.grammar]/14 refers to the result of `traits_inst.lookup_collatename` when it refers to a "collating element"; and if it does, it is unclear what input is to be used.

It is therefore unclear what the effect is if `traits_inst.lookup_collatename` substitutes another member of the equivalence class as its output.

For example, when processing `"[[.AA.]]"` as a pattern under a locale `da_DK.utf8`, what is the expected behaviour difference (if any) should `traits_inst.lookup_collatename` return, for `"AA"`, `"\u00C5"` (where `U+00C5` is A with ring, which sorts the same as `"AA"`)?

[2017-07 Toronto Monday issue prioritization]

Priority 3

Proposed resolution:

### 2990(i). `optional::value_type` is not always a value type

Section: 22.5.3 [optional.optional] Status: Open Submitter: Casey Carter Opened: 2017-06-27 Last modified: 2018-01-28

Priority: 3

View all other issues in [optional.optional].

View all issues with Open status.

Discussion:

`optional<T>::value_type` is `T`, which can be a cv-qualified object type. This is inconsistent with the uses of the name `value_type` elsewhere in the standard. We should either require `optional<T>::value_type` to be `remove_cv_t<T>` — a true value type — or rename the type alias to `element_type`.

[2017-07 Toronto Tuesday PM issue prioritization]

Priority 3; may also affect `array`

[2018-1-26 issues processing telecon]

Status to 'Open'

Proposed resolution:

This wording is relative to N4659.

1. Edit 22.5.3 [optional.optional], class template `optional` synopsis, as indicated:

   >     template <class T>
   >       class optional {
   >       public:
   >         using value_type = remove_cv_t<T>;
   >         […]
   >       };

### 3011(i). Requirements for `assert(E)` inconsistent with C

Section: 19.3 [assertions] Status: Open Submitter: Jonathan Wakely Opened: 2017-08-18 Last modified: 2018-08-20

Priority: 2

View all other issues in [assertions].

View all issues with Open status.

Discussion:

The C standard says that the expression in an `assert` must have a scalar type, and implies (or at least allows) that the condition is tested by comparison to zero. C++ says that the expression is a constant subexpression if it can be contextually converted to `bool`. Those ways to test the condition are not equivalent.

It's possible to have expressions that meet the C++ requirements for a constant subexpression, but fail to meet the C requirements, and so don't compile.

>     #include <stdlib.h>

>     // A toy implementation of assert:
>     #define assert(E) (void)(((E) != 0) || (abort(), 0))

>     struct X {
>       constexpr explicit operator bool() const { return true; }
>     };

>     constexpr bool f(const X& x) {
>       assert(x);
>       return true;
>     }

C++ says that `assert(x)` is a constant subexpression, but as it doesn't have scalar type it's not even a valid expression.

I think either 19.3.2 [cassert.syn] or 19.3.3 [assertions.assert] should repeat the requirement from C that E has scalar type, either normatively or in a note. We should also consider whether "contextually converted to `bool`" is the right condition, or if we should use comparison to zero instead.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 2; status to Open

Jonathan is discussing this with WG14

[2018-08-20, Jonathan comments]

This was reported to WG14 as N2207.

Proposed resolution:

### 3019(i). Presentation of "program defined classes derived from `error_category`" [syserr.errcat.derived] unclear and contains mistakes

Section: 19.5.3.4 [syserr.errcat.derived] Status: New Submitter: Thomas Köppe Opened: 2017-09-20 Last modified: 2017-11-09

Priority: 3

View all issues with New status.

Discussion:

The presentation of section [syserr.errcat.derived] is currently somewhat problematic:

- It is not clear why this section exists and what it is specifying. Presumably, it is the subject of the phrase "in this subclause" of the sibling section [syserr.errcat.overview], but that's confusing (because this would require the interpretation of "this subclause" as the containing superclause). It would be an improvement to say "shall behave as specified in [syserr.errcat.derived]" in the introduction.

- The current wording of [syserr.errcat.derived] requires that derived classes keep the `name` member function pure-virtual, making it impossible to have non-abstract derived classes. This appears to be an editorial error. Surely `name` should just not be required to be pure-virtual.

- There seems to be no requirement concerning the `message` virtual member function.

- We should use `override` rather than `virtual`.

- I would welcome a short, introductory paragraph in [syserr.errcat.derived] that states (non-redundantly) that the following are requirements on users' derived classes.

Partial wording proposal:

1. In 19.5.3.1 [syserr.errcat.overview] p1, change:

   > -1- The class `error_category` serves as a base class for types used to identify the source and encoding of a particular category of error code. Classes may be derived from `error_category` to support categories of errors in addition to those defined in this International Standard. Such classes shall behave as specified in this subclause19.5.3.4 [syserr.errcat.derived]. [Note: `error_category` objects are passed by reference, and two such objects are equal if they have the same address. This means that applications using custom `error_category` types should create a single object of each such type. — end note]

2. In 19.5.3.4 [syserr.errcat.derived], change:

   >     virtual const char* name() const noexcept override  = 0;

   > > -1- Returns: A string naming the error category.

   >     virtual error_condition default_error_condition(int ev) const noexcept override;

   > > -2- Returns: An object of type `error_condition` that corresponds to `ev`.

   >     virtual bool equivalent(int code, const error_condition& condition) const noexcept override;

   > > -3- Returns: `true` if, for the category of error represented by `*this`, `code` is considered equivalent to `condition`; otherwise, `false`.

   >     virtual bool equivalent(const error_code& code, int condition) const noexcept override;

   > > -4- Returns: `true` if, for the category of error represented by `*this`, `code` is considered equivalent to condition; otherwise, `false`.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3.

Jonathan to talk to Chris K and Walter about writing a paper describing the use of `error_code`, `error_condition` and defining your own.

Proposed resolution:

### 3021(i). [networking.ts] Relax pointer equivalence requirement for `ConstBufferSequence`

Section: 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Status: New Submitter: Vinnie Falco Opened: 2017-09-20 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

Addresses: networking.ts

The post-condition buffer sequence requirements mandate pointer equivalence. This means that a copies of buffer sequences must point to the same pieces of underlying memory. While this is appropriate for `MutableBufferSequence`, it is unnecessary for `ConstBufferSequence` and can actually prevent useful implementation strategies such as the following constant buffer sequence which avoids dynamic allocations:

>     /// A buffer sequence containing a chunk-encoding header
>     class chunk_size
>     {
>     public:
>         // Storage for the longest hex string we might need
>         class value_type
>         {
>             friend class chunk_size;

>             // First byte holds the length
>             char buf_[1 + 2 * sizeof(std::size_t)];

>             template<class = void>
>             void prepare(std::size_t n);

>             template<class OutIter>
>             static OutIter to_hex(OutIter last, std::size_t n)
>             {
>                 if (n == 0)
>                 {
>                     *--last = '0';
>                     return last;
>                 }
>                 while (n)
>                 {
>                     *--last = "0123456789abcdef"[n & 0xf];
>                     n >>= 4;
>                 }
>                 return last;
>             }
>         public:
>             operator boost::asio::const_buffer() const
>             {
>                 return {
>                     buf_ + sizeof(buf_) - buf_[0],
>                     static_cast(buf_[0])
>                 };
>             }
>         };

>         using const_iterator = value_type const*;

>         chunk_size(chunk_size const& other) = default;

>         /** Construct a chunk header

>             @param n The number of octets in this chunk.
>         */
>         chunk_size(std::size_t n)
>         {
>             value_.prepare(n);
>         }

>         const_iterator begin() const
>         {
>             return &value_;
>         }

>         const_iterator end() const
>         {
>             return begin() + 1;
>         }

>     private:
>         value_type value_;
>     };

Proposed resolution:

This wording is relative to N4588.

1. Modify 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Table 13 "`ConstBufferSequence` requirements" as indicated:

   > Table 13 — `ConstBufferSequence` requirements
   > expression | return type | assertion/note pre/post-condition
   > --- | --- | ---
   > `[…]`
   > `X u(x);` |  | post:
   >     equal(
   >       net::buffer_sequence_begin(x),
   >       net::buffer_sequence_end(x),
   >       net::buffer_sequence_begin(u),
   >       net::buffer_sequence_end(u),
   >       [](const typename X::value_type& v1,
   >          const typename X::value_type& v2)
   >         {
   >           const_buffer b1(v1);
   >           const_buffer b2(v2);
   >           return b1.data() == b2.data()
   >               && b1.size() == b2.size()
   >           return b1.size() == b2.size()
   >               && memcmp(b1.data(), b2.data(), b1.size()) == 0;
   >         })

### 3027(i). [networking.ts] `DynamicBuffer` `prepare` exception specification

Section: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] Status: New Submitter: Vinnie Falco Opened: 2017-10-16 Last modified: 2020-09-06

Priority: 3

View other active issues in [networking.ts::buffer.reqmts.dynamicbuffer].

View all other issues in [networking.ts::buffer.reqmts.dynamicbuffer].

View all issues with New status.

Discussion:

Addresses: networking.ts

The current wording for the `DynamicBuffer` `prepare` member function implies that `std::length_error` is the only allowable thrown exception. This should be changed to reflect that any exception may be thrown, with `std::length_error` thrown in particular when `size() + n` exceeds `max_size()`.

[2017-11-08]

Priority set to 3 after five votes on mailing list

Proposed resolution:

This wording is relative to N4588.

1. Change 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], Table 14 "DynamicBuffer requirements", as indicated:

   > Table 14 — `DynamicBuffer` requirements
   > expression | return type | assertion/note pre/post-condition
   > --- | --- | ---
   > `[…]`
   > `x.prepare(n)` | `X::mutable_buffers_type` | Returns a mutable buffer sequence `u` representing the writable bytes, and where `buffer_size(u) == n` . The dynamic buffer reallocates memory as required. All constant or mutable buffer sequences previously obtained using `data()` or `prepare()` are invalidated. Throws: `length_error` if `size() + n` exceeds `max_size()` or any other exception if the request cannot otherwise be satisfied.

### 3029(i). `pop_heap` over-constrains input

Section: 26.8.8.3 [pop.heap] Status: Open Submitter: Mathias Stearn Opened: 2017-11-04 Last modified: 2020-09-06

Priority: 3

View all issues with Open status.

Discussion:

The spec for `<algorithms>` `pop_heap` includes

> -1- Requires: The range `[first, last)` shall be a valid non-empty heap.

This has the unfortunate consequence that to pop a value and push a new value is substantially less efficient than necessary. The popped value must be extracted by `pop_heap` (using up to 2 log `N` compares and swaps), and then, in `push_heap`, the new value must be inserted (for up to `N` compares and swaps, but more usually something like log `N`).

Simply relaxing the requirement to

> -1- Requires: The range `[first, last - 1)` shall be a valid heap.

enables use of `pop_heap` in an integrated push-and-pop operation, with less than half the number of expected compare and swap operations. Furthermore, if, as is often the case, the newly pushed value would have ended up at position `first`, the push/pop operation could complete in time `𝒪(1)`, instead of (3 log `N`).

The effect of the proposed relaxation on existing library implementations would be minimal in the extreme, and on existing user code nil. The base algorithm code remains exactly identical. The only changes needed would be to any instrumentation in a debugging version of the library, which would just need to relax its check, and to test suites that should exercise the newly tolerated input.

Users today are tempted to get the improved performance by relying on existing implementations' tacit tolerance of input that only satisfies the proposed, relaxed requirements. In fact, the cppreference.com page on `pop_heap` offers no hint that this usage is not already allowed. This change would bless such reliance as formally permitted.

After this change, minor extensions to `std::priority_queue` would enable it to take advantage of the newly efficient operation, perhaps:

>     void pop_push(const Type&);
>     void pop_push(Type&&);
>     template <class... Args> void pop_emplace(Args&&... args);

These will appear in a formal proposal if the resolution is accepted.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3

[2017-11 Albuquerque Saturday issues processing]

status to Open; Marshall to review

Proposed resolution:

This wording is relative to N4700.

1. Change 26.8.8.3 [pop.heap] as indicated:

   >     template<class RandomAccessIterator>
   >       void pop_heap(RandomAccessIterator first, RandomAccessIterator last);
   >     template<class RandomAccessIterator, class Compare>
   >       void pop_heap(RandomAccessIterator first, RandomAccessIterator last,
   >                     Compare comp);

   > > -1- Requires: The range `[first, last - 1)` shall be a valid non-empty heap. `RandomAccessIterator` shall satisfy the requirements of `ValueSwappable` (16.4.4.3 [swappable.requirements]). The type of `*first` shall satisfy the requirements of `MoveConstructible` (Table 23) and of `MoveAssignable` (Table 25).

### 3044(i). Strange specification of `max_size()` for an allocator

Section: 16.4.4.6 [allocator.requirements] Status: New Submitter: Jon Cohen Opened: 2017-12-06 Last modified: 2025-10-18

Priority: 3

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

Table 31 in the C++17 standard specifies `X::max_size()` (where `X` is an allocator type) as "The largest value that can meaningfully be passed to `X::allocate()`". Noticeably missing is the statement "Throws: Nothing".

As an example of why this is an issue, note that `vector::max_size()` and `allocator_traits::max_size()` are both marked `noexcept`. We must then interpret `max_size()` as being allowed to sometimes call `std::terminate`, or else `{vector, allocator_traits, ...}::max_size()` must be allowed to directly calculate `numeric_limits<size_type>::max() / sizeof(value_type)` instead of querying the allocator, even if `Alloc::max_size()` exists. This seems like a bug in the wording for the requirements of `max_size()` in an allocator type. I think an issue should be opened on this subject to add Throws: Nothing or similar to the requirements of `max_size()` for an allocator.

As an example consider writing up a framework to test the exception-safety of types in a given framework, since they were all written in an exception-free environment. One of the types in the framework is an allocator which, in a controlled way, can throw an exception at any point where it is allowed by the standard. It's important that the test framework be as pedantic as possible, so the allocator type throws on `max_size()`, since it is currently allowed to by the standard. When a reasonable `vector` implementation (at least those in libstdc++ and msvc) is, for example, asked to construct a `vector` from an `initializer_list`, it will call `allocator_traits<Alloc>::max_size()`, which will terminate the program because the exception thrown in `Alloc::max_size()` propagated through the `noexcept` traits function. Although this is conformant behavior, I think it's a bug in the standard that a function as benign as `max_size()` can terminate the program in this manner, and I think the fix is that a conformant allocator should be required to supply a non-throwing `max_size()` member function.

Daniel:

This problem was shortly discussed during review of LWG 2162(i) (see comment 2012-08-05). At that time the more drastic but also more consistent requirement that an allocator's `max_size` function shall not throw exceptions has not been added. IMO this position should be reconsidered to follow the spirit of the new issue LWG 3044(i).

[2018-01; Priority set to 3 after mailing list discussion]

[2018-08-21, Jonathan comments and provides wording]

The phrase "the largest value that can meaningfully be passed to `X::allocate()`" is meaningless. Is it a requirement on the caller, so that larger values must not be passed? Or a hint from the allocator implementor that larger values will produce a `bad_alloc` exception? Can the return value change dynamically, based on the free memory available to the allocator?! — LWG 197(i) says it can't change.

As noted in the LWG 2162(i) comments, we don't currently guarantee it can be called on a `const` object (so `allocator_traits` will not use the allocator's `max_size()` if it's non-`const`, although that was unclear before DR 2284(i)). In addition to adding "Throws: nothing" we should ensure it's callable on `const` lvalues, and clarify what "meaningfully" means and who is supposed to care about it. My proposed resolution doesn't achieve all of this, but is a start.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4762.

> 1. Change 16.4.4.6 [allocator.requirements], Table 32 — "Descriptive variable definitions", as indicated:

>    > Table 32 — Descriptive variable definitions
>    > Variable | Definition
>    > --- | ---
>    > `T, U, C` | any cv-unqualified object type (3.9)
>    > `…`
>    > `a, a1, a2` | lvalues of type `X`
>    > `a3` | an lvalue of type `const X`
>    > `…`

> 2. Change 16.4.4.6 [allocator.requirements], Table 33 — "Cpp17Allocator requirements", as indicated:

>    > Table 33 — Cpp17Allocator requirements
>    > Expression | Return type | Assertion/note pre-/post-condition | Default
>    > --- | --- | --- | ---
>    > `…`
>    > `a3.max_size()` | `X::size_type` | the largest value that can meaningfully be passed to `X::allocate()`. [Note: Larger values might cause an exception to be thrown. — end note] Throws: Nothing. | `numeric_limits<size_type>::max() / sizeof(value_type)`
>    > `…`

[2022-04-25; Daniel rebases wording on N4910]

Previous resolution [SUPERSEDED]

This wording is relative to N4910.

1. Change 16.4.4.6.1 [allocator.requirements.general] as indicated:

   > -2- In subclause 16.4.4.6 [allocator.requirements],

   > 1. (2.1) — […]

   > 2. […]

   > 3. (2.6) — `a`, `a1`, `a2` denote lvalues of type `X`,

   > 4. (?.?) — `a3` denotes an lvalue of type `const X`,

   > 5. […]

   > […]

   >     a3.max_size()

   > > -50- Result: `X::size_type`

   > > -51- Returns: The largest value that can meaningfully be passed to `X::allocate()`.

   > > [Note: Larger values might cause an exception to be thrown. — end note]

   > > -?- Throws: Nothing.

   > > -52- Remarks: Default: `numeric_limits<size_type>::max() / sizeof(value_type)`

[2025-10-10; Jonathan provides improved wording]

[2025-10-18; Jonathan removes the note from the wording]

After reflector discussion, I've remove the following note from the proposed resolution: [Note: Larger values can cause an exception to be thrown. — end note]

I also suggested an alternative: [Note: Containers and other users of allocators can use this function to avoid making calls to `X::allocate` which are unlikely ever to succeed. — end note]

Those notes will be recorded here, but are not part of the proposed resolution.

Proposed resolution:

This wording is relative to N5014.

1. Change 16.4.4.6.1 [allocator.requirements.general] as indicated:

   > -2- In subclause 16.4.4.6 [allocator.requirements],

   > 1. (2.1) — […]

   > 2. […]

   > 3. (2.6) — `a`, `a1`, `a2` denote lvalues of type `X`,

   > 4. (?.?) — `a3` denotes an lvalue of type (possibly const) `X`,

   > 5. […]

   > […]

   >     a3.max_size()

   > > -50- Result: `X::size_type`

   > > -51- Returns: The largest value that can meaningfully be passed to `X::allocate()`.

   > > -?- Throws: Nothing.

   > > -52- Remarks: Default: `numeric_limits<size_type>::max() / sizeof(value_type)`

### 3046(i). Do not require `reference_wrapper` to support non-referenceable function types

Section: 22.10.6 [refwrap] Status: New Submitter: Jonathan Wakely Opened: 2017-12-14 Last modified: 2020-09-06

Priority: 3

View all other issues in [refwrap].

View all issues with New status.

Discussion:

[refwrap] says that `reference_wrapper<T>` is a "wrapper around a reference to an object or function of type `T`" but this doesn't actually constrain it, and doesn't forbid non-referenceable function types like `int() const`.

There is no way to construct a `reference_wrapper<int() const>` but implementations are required to provide partial specializations for functions with cv-qualifiers and ref-qualifiers in order to define a nested `result_type`. It should be undefined to instantiate `reference_wrapper<T>` with a non-referenceable type, or with a reference type (since references to references are not possible). Making it undefined (rather than ill-formed or unspecified) means implementations are not required to diagnose such invalid specializations, but also don't have to go to the effort of supporting weak result types etc.

[2018-01; Priority set to 3 after mailing list discussion]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4713.

> 1. Modify 22.10.6 [refwrap] as indicated:

>    > -1- `reference_wrapper<T>` is a `CopyConstructible` and `CopyAssignable` wrapper around a reference to an object or function of type `T`. `T` shall be a referenceable type (3.46 [defns.referenceable]) that is not a reference type.

>    > -2- `reference_wrapper<T>` shall be a trivially copyable type (6.9 [basic.types]).

[2019-03-15; Daniel comments and provides revised wording]

The current wording is now far behind the working draft and a synchronization is therefore recommended. In particular, with the acceptance of P0357R1, the specification of `reference_wrapper` has no longer any weak result type. Second, I would like to concur with a remark from Tomasz to change the wording to replace the undefined behavior by an ill-formed program instead, because every attempt to instantiate the definition of `reference_wrapper` will instantiate its member declarations, and this would cause the program to become ill-formed anyway because of the illegal formation of references to non-referenceable function types for member functions such as `T& get() const noexcept`.

As concrete wording suggestion I would recommend wording that ensures that an ill-formed program is only required when a specialization of `reference_wrapper` is instantiated, because in the absence of a constrained template parameter we shouldn't require implementations to diagnose even forming the name of a `reference_wrapper` specialization such as in the following example:

>     using X = reference_wrapper<int() const>;

The wording below does not take advantage of a Mandates: element to prevent a dependency on LWG 3193(i) and because such an element is rarely used to specify class templates. If the committee wishes to use such an element, the equivalent wording would be:

> Mandates: `reference_wrapper` is instantiated with a referenceable type (3.46 [defns.referenceable]) as the argument for the template parameter `T`.

Proposed resolution:

This wording is relative to N4800.

1. Modify 22.10.6 [refwrap] as indicated:

   > -1- `reference_wrapper<T>` is a Cpp17CopyConstructible and Cpp17CopyAssignable wrapper around a reference to an object or function of type `T`. If `reference_wrapper` is instantiated with a non-referenceable type (3.46 [defns.referenceable]) as the argument for the template parameter `T`, the program is ill-formed.

   > -2- `reference_wrapper<T>` is a trivially copyable type (6.9 [basic.types]).

   > -3- The template parameter `T` of `reference_wrapper` may be an incomplete type.

### 3047(i). `atomic` compound assignment operators can cause undefined behavior when corresponding `fetch_meow` members don't

Section: 32.5.8.3 [atomics.types.int], 32.5.8.5 [atomics.types.pointer], 32.5.8.6 [atomics.types.memop] Status: New Submitter: Tim Song Opened: 2017-12-15 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

Given `atomic<int> meow{INT_MAX};`, `meow.fetch_add(1)` has well-defined behavior because 32.5.8.3 [atomics.types.int] p7 says that

> Remarks: For signed integer types, arithmetic is defined to use two's complement representation. There are no undefined results.

but `meow += 1` and `++meow` have undefined behavior, because these operator functions are defined (by, respectively, 32.5.8.3 [atomics.types.int] p8 and 32.5.8.6 [atomics.types.memop]) to be equivalent to `return fetch_add(1) + 1;`, and so the addition of 1 to the result of `fetch_add` — which causes an integer overflow in this case — occurs outside the protection of `fetch_add` magic. Additionally, the return value might differ from what `fetch_add` actually wrote since that addition isn't required to use two's complement. This seems like a trap for the unwary. Is it intended?

A similar issue affects the `atomic<T*>` partial specialization for pointers.

[2018-01; Priority set to 3 after mailing list discussion]

[2019-04-15; JF Bastien comments and provides wording]

As discussed by LWG during the San Diego 2018 meeting, Jens removed LWG 3047 from "P1236R1: Alternative Wording for P 0907R4 Signed Integers are Two's Complement".

Proposed resolution:

This wording is relative to N4810.

1. Modify 32.5.7.3 [atomics.ref.int] as indicated:

   >     integral operator op=(integral operand) const noexcept;

   > > -7- Effects: Equivalent to: `return static_cast<integral>(static_cast<make_unsigned_t<integral>>(fetch_key(operand)) op static_cast<make_unsigned_t<integral>>(operand));`

2. Modify 32.5.7.6 [atomics.ref.memop] as indicated:

   >     T* operator++() const noexcept;

   > > -3- Effects: Equivalent to: `return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_add(1)) + static_cast<make_unsigned_t<T>>(1));`

   >     T* operator--(int) const noexcept;

   > > -4- Effects: Equivalent to: `return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_sub(1)) - static_cast<make_unsigned_t<T>>(1));`

3. Modify 32.5.8.3 [atomics.types.int] as indicated:

   >     T operator op=(T operand) volatile noexcept;
   >     T operator op=(T operand) noexcept;

   > > -8- Effects: Equivalent to: `return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_key(operand)) op static_cast<make_unsigned_t<T>>(operand));`

   > [Drafting note: `atomic<integral>`'s working for `operator++/operator--` is shared with `atomic<T*>`. — end drafting note]

   > [Drafting note: `atomic<floating-point>` seems to be correct, LWG should confirm that it is. — end drafting note]

4. Modify 32.5.8.5 [atomics.types.pointer] as indicated:

   >     T* operator op=(ptrdiff_t operand) volatile noexcept;
   >     T* operator op=(ptrdiff_t operand) noexcept;

   > > -8- Effects: Equivalent to: `return reinterpret_cast<T*>(reinterpret_cast<ptrdiff_t>(fetch_key(operand)) op operand);`

   > > Remarks: The result may be an undefined address, but the operations otherwise have no undefined behavior.

5. Modify 32.5.8.6 [atomics.types.memop] as indicated:

   >     T operator++() volatile noexcept;
   >     T operator++() noexcept;

   > > -3- Effects: Equivalent to: `return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_add(1)) + static_cast<make_unsigned_t<T>>(1));`

   >     T operator--() volatile noexcept;
   >     T operator--() noexcept;

   > > -4- Effects: Equivalent to: `return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_sub(1)) - static_cast<make_unsigned_t<T>>(1));`

   > [Drafting note: Alternatively, LWG may want to separate the integral overload of `operator++/operator--` from that of `atomic<T*>`. end drafting note]

### 3049(i). Missing wording allowing algorithms to use copies of function objects as substitutes for their parameters

Section: 26.2 [algorithms.requirements] Status: Open Submitter: Jared Hoberock Opened: 2017-12-04 Last modified: 2022-04-25

Priority: 3

View other active issues in [algorithms.requirements].

View all other issues in [algorithms.requirements].

View all issues with Open status.

Discussion:

When designing the parallel algorithms library, we intended for parallel algorithms to copy their function objects parameters when it is possible and useful to do so, but there doesn't appear to be any wording to enable that latitude. To the contrary, algorithm specifications refer to their function object parameters by name, implying that a copy of the parameter may not be used as a substitute.

This was noticed when Billy O'Neal observed that parallel `generate()` did not share parallel `for_each()` and `for_each_n()`'s special requirement for a `CopyConstructible` user-provided function object.

This `CopyConstructible Function` requirement was added to relax legacy `for_each()`'s `MoveConstructible Function` requirement to allow parallel implementations to make copies as necessary. All parallel algorithms need similar permissions, but a strong requirement for `CopyConstructible` in all algorithms is too restrictive.

What we require is to allow algorithm implementations to use copies of function objects as substitutes for their original parameters, while not requiring that all function object parameters be copyable.

Casey Carter noted that 26.2 [algorithms.requirements] p8 grants permission to all algorithms to copy their function object parameters. However, this paragraph is not normative and does not indicate how the algorithm is allowed to use such copies. Additionally, it does not specify which algorithm parameters are the ones called out as function objects. For example, 26.7.7 [alg.generate] refers to `gen` as a function object, but 26.6.5 [alg.foreach] does not refer to `f` as a function object. All the other types of callable algorithm parameters (i.e. `Predicate`, `BinaryPredicate`, `Compare`, `UnaryOperation`, `BinaryOperation`, `BinaryOperation1`, and `BinaryOperation2`) are defined to be function objects in 26.2 [algorithms.requirements] and 26.3.2 [algorithms.parallel.user]. This list intentionally omits `Function` and `Generator` by design.

A potential resolution would introduce normative wording to explicitly allow algorithms to use copies of function object parameters as substitutes for their function object parameters, and remove ambiguity in algorithm specifications about which parameters are function objects.

[2018-01; Priority set to 3 after mailing list discussion]

[2018-3-14 Wednesday evening issues processing; move to Open]

We thought that the notes in [alg.foreach]/1 and /11 should be unwrapped as well. Bryce to work with Jared on updated wording.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4713.

> 1. Modify 26.2 [algorithms.requirements] as indicated:

>    > -8- [Note: Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. When an algorithm's specification requires the invocation of a function object parameter, such a copy may be invoked as a substitute for the original function object parameter. [Note: This implies that copyable user-supplied function objects should not rely on their identity. Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such as `reference_wrapper<T>` (22.10.6 [refwrap]), or some equivalent solution. — end note]

> 2. Modify 26.6.5 [alg.foreach] as indicated:

>    >     template<class InputIterator, class Function>
>    >       constexpr Function for_each(InputIterator first, InputIterator last, Function f);

>    > > […]

>    > > -2- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, last)`, […]

>    > > […]

>    >     template<class ExecutionPolicy, class ForwardIterator, class Function>
>    >       void for_each(ExecutionPolicy&& exec,
>    >                     ForwardIterator first, ForwardIterator last,
>    >                     Function f);

>    > > -6- Requires: `Function` shall meet the requirements of `CopyConstructible`.

>    > > -7- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, last)`. […]

>    > > […]

>    >     template<class InputIterator, class Size, class Function>
>    >     constexpr InputIterator for_each_n(InputIterator first, Size n, Function f);

>    > > […]

>    > > -13- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, first + n)` in order. […]

>    > > […]

>    >     template<class ExecutionPolicy, class ForwardIterator, class Size, class Function>
>    >       ForwardIterator for_each_n(ExecutionPolicy&& exec, ForwardIterator first, Size n,
>    >                                  Function f);

>    > > -16- Requires: `Function` shall meet the requirements of `CopyConstructible`.

>    > > […]

>    > > -18- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, first + n).` […]

>    > > […]

[2022-04-25; Daniel rebases wording on N4910]

The previously refactored note term "can" in 26.2 [algorithms.requirements] p10 has been reverted to "permitted" to specify a normative implementation freedom.

[2022-04-25; Daniel comments]

Bryce and Jared have unassigned from this issue.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.2 [algorithms.requirements] as indicated:

   > -10- [Note 2: Unless otherwise specified, algorithms that take function objects as arguments canare permitted to copy those function objects freely. When an algorithm's specification requires the invocation of a function object parameter, such a copy may be invoked as a substitute for the original function object parameter. [Note: This implies that copyable user-supplied function objects should not rely on their identity. If object identity is important, a wrapper class that points to a noncopied implementation object such as `reference_wrapper<T>` (22.10.6 [refwrap]), or some equivalent solution, can be used. — end note]

2. Modify 26.6.5 [alg.foreach] as indicated:

   >     template<class InputIterator, class Function>
   >       constexpr Function for_each(InputIterator first, InputIterator last, Function f);

   > > […]

   > > -2- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, last)`, […]

   > > […]

   >     template<class ExecutionPolicy, class ForwardIterator, class Function>
   >       void for_each(ExecutionPolicy&& exec,
   >                     ForwardIterator first, ForwardIterator last,
   >                     Function f);

   > > -6- Preconditions: `Function` meets the Cpp17CopyConstructible requirements.

   > > -7- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, last)`. […]

   > > […]

   >     template<class InputIterator, class Size, class Function>
   >     constexpr InputIterator for_each_n(InputIterator first, Size n, Function f);

   > > […]

   > > -18- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, first + n)` in order. […]

   > > […]

   >     template<class ExecutionPolicy, class ForwardIterator, class Size, class Function>
   >       ForwardIterator for_each_n(ExecutionPolicy&& exec, ForwardIterator first, Size n,
   >                                  Function f);

   > > […]

   > > -22- Preconditions: `n >= 0` is `true`. `Function` meets the `Cpp17CopyConstructible` requirements.

   > > -23- Effects: Applies the function object `f` to the result of dereferencing every iterator in the range `[first, first + n).` […]

   > > […]

### 3053(i). Prohibit `error_code` construction from rvalues of `error_category`

Section: 19.5.4.1 [syserr.errcode.overview] Status: Ready Submitter: Antony Polukhin Opened: 2018-01-24 Last modified: 2026-06-15

Priority: 3

View all other issues in [syserr.errcode.overview].

View all issues with Ready status.

Discussion:

Constructor `error_code(int val, const error_category& cat)` and member function `void assign(int val, const error_category& cat)` could be misused if a custom `error_category` is provided:

>     error_code ec{1, test_category{}}; // ec holds a pointer/reference to a temporary

[2018-06-18 after reflector discussion]

Priority set to 3

[Brno 2026-06-10; LWG agreed this is an improvement, need to change `error_condition` too.]

Previous resolution [SUPERSEDED]

This wording is relative to N4713.

1. Modify 19.5.4.1 [syserr.errcode.overview] as indicated:

   >     namespace std {
   >       class error_code {
   >       public:
   >         // 19.5.4.2 [syserr.errcode.constructors], constructors
   >         […]
   >         error_code(int val, const error_category& cat) noexcept;
   >         error_code(int val, const error_category&& cat) = delete;
   >         […]
   >         // 19.5.4.3 [syserr.errcode.modifiers], modifiers
   >         void assign(int val, const error_category& cat) noexcept;
   >         void assign(int val, const error_category&& cat) = delete;
   >         […]
   >       };
   >       […]
   >     }

[2026-06-10; Tim provides new wording]

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5060.

1. Modify 19.5.4.1 [syserr.errcode.overview] as indicated:

   >     namespace std {
   >       class error_code {
   >       public:
   >         // 19.5.4.2 [syserr.errcode.constructors], constructors
   >         […]
   >         error_code(int val, const error_category& cat) noexcept;
   >         error_code(int val, const error_category&& cat) = delete;
   >         […]
   >         // 19.5.4.3 [syserr.errcode.modifiers], modifiers
   >         void assign(int val, const error_category& cat) noexcept;
   >         void assign(int val, const error_category&& cat) = delete;
   >         […]
   >       };
   >       […]
   >     }

2. Modify 19.5.5.1 [syserr.errcondition.overview] as indicated:

   >     namespace std {
   >       class error_condition {
   >       public:
   >         // 19.5.5.2 [syserr.errcondition.constructors], constructors
   >         […]
   >         error_condition(int val, const error_category& cat) noexcept;
   >         error_condition(int val, const error_category&& cat) = delete;
   >         […]
   >         // 19.5.5.3 [syserr.errcondition.modifiers], modifiers
   >         void assign(int val, const error_category& cat) noexcept;
   >         void assign(int val, const error_category&& cat) = delete;
   >         […]
   >       };
   >       […]
   >     }

### 3056(i). `copy_file()` copies which attributes?

Section: 31.12.13.5 [fs.op.copy.file] Status: New Submitter: Davis Herring Opened: 2018-01-26 Last modified: 2020-09-06

Priority: 3

View all other issues in [fs.op.copy.file].

View all issues with New status.

Discussion:

(To resolve C++17 CD comment Late 25.) It is not stated which attributes are copied by `copy_file()`.

[2018-1-26 issues processing telecon]

Priority 3

Proposed resolution:

This wording is relative to N4713.

1. Modify 31.12.13.5 [fs.op.copy.file] as indicated:

   > Rationale:

   > The attributes specified are the useful subset of the attributes that can be queried in C++17. Existing practice is complicated: POSIX "cp -p" attempts to preserve user/group IDs, for instance, but cannot in general do so, and `setuid/setgid` permissions may be stripped.

   >     bool copy_file(const path& from, const path& to, copy_options options);
   >     bool copy_file(const path& from, const path& to, copy_options options,
   >                    error_code& ec) noexcept;

   > > […]

   > > 1. […]

   > > 2. (4.2) — Otherwise, copy the contents, permissions, and data modification time and attributes of the file `from` resolves to, to the file `to` resolves to, if:

   > > 3. […]

   > > 4. 1. (4.2.3) — `(options & copy_options::update_existing) != copy_options::none` and `from` is more recent than `to`, determined as if by use of the `last_write_time` function (31.12.13.26 [fs.op.last.write.time]).

   > >    Other implementation-defined attributes may be copied. Failure (or partial failure) to copy attributes is not an error.

   > > 5. […]

   > > […]

### 3057(i). Correct `copy_options` handling

Section: 31.12.13.4 [fs.op.copy] Status: Open Submitter: Davis Herring Opened: 2018-01-29 Last modified: 2020-09-06

Priority: 2

View all other issues in [fs.op.copy].

View all issues with Open status.

Discussion:

(The resolution of #3 resolves part of C++17 NB comment US 36.)

The handling of several options for `filesystem::copy()` is wrong:

1. Single-level directory copying is silently suppressed by any flag other than `copy_options::recursive` (even `copy_options::directories_only`). Single-level directory copying operates via using some unspecified flag to trigger this misfeature.

2. `copy_options::create_symlinks` and `copy_options::skip_symlinks` affect the interpretation of the destination name; the latter shouldn't ever, and the former should affect only broken symlinks (since it would want to replace them).

3. The `copy_options` groups for existing target files and the form of copying are consulted only for creating regular files.

4. `copy("file", "dir")` creates dir/file, but `copy("symlink", "dir", copy_options::copy_symlinks)` fails.

5. If a symlink is encountered with `copy_options::copy_symlinks` and `copy_options::create_symlinks`, the latter flag is ignored (but its otherwise sensible restriction to absolute paths applies) rather than the former.

6. `copy_options::create_symlinks` without `copy_options::copy_symlinks` fails if it encounters a symlink. (This is particularly damaging for recursive operation.)

This issue, since it replaces so much text, also addresses two error-handling concerns in passing:

1. The significance of `equivalent(from, to)` failing is unspecified. (Ignoring such failures entirely would make dangerous those operations that replace the target with a link.)

2. Copying a directory involves several operations. When an `error_code` is being used, the process continues past errors and (because successful functions call `ec.clear()`) may suppress them.

This expands on the resolution for LWG 2681(i).

This also addresses the same issue as LWG 2682(i), but has a different result (based on the fact that the Example successfully copies directories to new, non-existent names).

[2018-06; Rapperswil Wednesday evening, discussing LWG 2682(i)]

JW: can we use the words we are shipping already since two years?
BO: what we got is better than what we had before
no objection to moving to Ready
ACTION move to Ready
ACTION link LWG 2682(i) and LWG 3057 and set a priority 2 and look at 3057 in San Diego

[2018-11 San Diego Thursday night issue processing]

Need to gather implementation experience; revisit in Kona. Status to Open.

[2018-11-13; Billy O'Neal comments]

I (Billy O'Neal) prefer Davis' solution to LWG 3057, as I think the wording follows the meaning of the individual enum values more closely, and enables more scenarios to function correctly instead of reporting such cases as errors.

However, I don't want to adopt that wording as is because it requires my implementation to detect errors in places that force us to do a bunch of extra system calls, and I don't believe those specific ways error handling happens is relevant to what the copy API wants to do.

Ideally, the wording would be structured such that it said "here's a list of error conditions, if they happen we aren't going to tell you when exactly they are detected" and then listed the behavior irrespective of when errors happen. That way implementations can do the error checks when it makes sense according to what their system APIs report. For example, anything that requires symlink resolution is very expensive on my platform so I'd want to be able to defer anything related to status (rather than `symlink_status`) to after I've detected that there's actually a symlink (or junction) involved.

Proposed resolution:

This wording is relative to N4750.

1. Modify Table 115 — "Enum class `copy_options`" as indicated:

   > Option group controlling `copy` and `copy_file` function effects for existing target files
   > Constant | Meaning
   > […] | […]

2. Modify 31.12.13.4 [fs.op.copy] as indicated:

   > Rationale:

   > POSIX.1-2008 allows the implementation to create hard links "to" symbolic links, and provides `linkat()` to choose between the symlink and its target.

   > 31.12.13.4 [fs.op.copy]/4.9 is redundant given 31.12.5 [fs.err.report]/3.1.

   >     void copy(const path& from, const path& to, copy_options options);
   >     void copy(const path& from, const path& to, copy_options options,
   >               error_code& ec) noexcept;

   > > -3- Requires: At most one element from each option group (31.12.8.3 [fs.enum.copy.opts]) is set in `options`.

   > > -4- Effects: Before the first use of `f` and `t`:

   > > 1. (4.1) — If […]

   > > 2. […]

   > > 3. (4.10) — Otherwise, no effects.

   > > If each is needed below,

   > > >     auto linkf = (options & (copy_options::copy_symlinks |
   > > >                              copy_options::skip_symlinks)) != copy_options::none;
   > > >     auto f = linkf ? symlink_status(from) : status(from), t = status(to);
   > > >     auto to2 = !is_directory(f) && is_directory(t) ? to/from.filename() : to.
   > > >     bool linkt = (options & (copy_options::create_symlinks |
   > > >                              copy_options::create_hard_links)) != copy_options::none ||
   > > >                 is_symlink(f);
   > > >     auto t2 = linkt ? symlink_status(to2) : status(to2);

   > > > [Drafting note: `copy_options::create_symlinks` is intentionally omitted for linkf; it may simply have been a typo for `copy_options::copy_symlinks` (which was added by LWG 2681(i)) since at least N3940.]

   > > Effects are then as follows:

   > > 1. (?.?) — If `f.type()` or `t.type()` is an implementation-defined file type [fs.enum.file_type], then the effects are implementation-defined.

   > >    > [Drafting note: the text between the previous drafting note and this one is the only unchanged text under /4.]

   > > 2. (?.?) — Otherwise, if `exists(f)` is `false`, report an error as specified in 31.12.5 [fs.err.report].

   > > 3. (?.?) — Otherwise, do nothing if

   > >    1. (?.?.?) — `(options & copy_options::directories_only) != copy_options::none` and `is_directory(f)` is `false`, or

   > >    2. (?.?.?) — `(options & copy_options::skip_symlinks) != copy_options::none` and `is_symlink(f)` is `true`, or

   > >    3. (?.?.?) — `(options & copy_options::skip_existing) != copy_options::none` and `exists(t2)` is `true`.

   > > 4. (?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:

   > >    1. (?.?.?) — `is_other(f) || is_other(t2)` is `true`, or

   > >    2. (?.?.?) — `exists(t2) && exists(from) == exists(to2) && equivalent(from, to)` is `true`.

   > > 5. (?.?) — Otherwise, if `is_directory(f)` is `true`, then:

   > >    1. (?.?.?) — `create_directory(to, from)`.

   > >    2. (?.?.?) — If `(options & copy_options::recursive) != copy_options::none` or if `(options & copy_options::directories_only) == copy_options::none`, iterate over the files in `from`, as if by

   > >       >     for (const directory_entry& x : directory_iterator(from))
   > >       >       if ((options & copy_options::recursive) != copy_options::none ||
   > >       >          !is_directory(linkf ? symlink_status(x.path()) : status(x.path())))
   > >       >            copy(x.path(), to/x.path().filename(), options);

   > > 6. (?.?) — Otherwise, do nothing if `(options & copy_options::update_existing) != copy_options::none, exists(to2)` is `true`, and `from` is not more recent than `to2`, determined as if by use of the `last_write_time` function ( [fs.op.last_write_time]).

   > > 7. (?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:

   > >    1. (?.?.?) — `is_directory(t2)` is `true`, or

   > >    2. (?.?.?) — `(options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none` and `exists(t2)` is `true`.

   > > 8. (?.?) — Otherwise, if `linkt` is `true`, then:

   > >    1. (?.?.?) — `remove(to2)` if an existing `to2` would prevent the following link creation.

   > >    2. (?.?.?) — If `(options & copy_options::create_symlinks) != copy_options::none`, `create_symlink(from, to2)`. [Note: If `from` is a symbolic link, it is not followed. — end note]

   > >    3. (?.?.?) — Otherwise, if `(options & copy_options::create_hard_links) != copy_options::none`, then create a hard link to `from`, if `linkf` is `true`, or else to the file that `from` resolves to. [Note: Not all file systems that support hard links and symbolic links support creating hard links to symbolic links. — end note]

   > >    4. (?.?.?) — Otherwise, `copy_symlink(from, to2)`.

   > > 9. (?.?) — Otherwise, `copy_file(from, to2, options)`.

   > > -5- Throws: As specified in 31.12.5 [fs.err.report].

   > > -6- Remarks: For the signature with argument `ec`, any library functions called by the implementation shall have an `error_code` argument if applicable. If any such function fails, `copy` returns immediately without (further) modifying `ec`.

### 3059(i). Wrong requirements for map-like associative container assignment?

Section: 23.2 [container.requirements] Status: New Submitter: Richard Smith Opened: 2018-02-05 Last modified: 2018-02-14

Priority: 3

View all other issues in [container.requirements].

View all issues with New status.

Discussion:

What are the requirements for

>     a = b;

... where `a` and `b` are of map-like associative container type (`map`, `multimap`, `unordered_map`, `unordered_multimap`)?

The general container requirements say just:

>     r = a  // Postconditions: r == a

(Incidentally, earlier in the table, there is a clear error: the general container requirements permit "`a = rv`" for assignment from an rvalue, but "`a`" here is a potentially-const container. Oops.) Oddly. there are no requirements at all on `T` here.

The allocator-aware container requirements add:

>     a = t  // Requires: T is CopyInsertable into X and CopyAssignable.

... where `T` is the container's `value_type`, that is, `pair<const key_type, mapped_type>`. Note that such a pair is not `CopyAssignable` for "normal" key types that disallow assignment to `const` objects. They also add:

>     a = rv  // Requires: if !POCMA, T is MoveInsertable into X and MoveAssignable.

... which has the same problem in the !POCMA case.

The associative container requirements and unordered associative container requirements have a similar problem for assignment from an initializer list:

>     a = il  // Requires: value_type is CopyInsertable into X and CopyAssignable.

Presumably these assignments are intended to actually work, but what are the intended constraints? Do we wish to allow implementations to perform node reuse for these map-like containers? Presumably yes, and if so, the `key_type` portion of the node must be assigned as well as the `value_type` portion (for instance, with whatever implementation technique is used to power `node_handle`) as we cannot assume that key equivalence (or, for `unordered_*map`, even key equality) implies substitutability.

I think, then, that the associative container requirements and unordered associative container requirements should specify different requirements for the "`a = t`", "`a = rv`", and "`a = il`" for the map-like containers; specifically:

- for "`a = t`" and "`a = il`", we should require that `value_type` is `CopyInsertable` into `X`, and `key_type` and `mapped_type` are `CopyAssignable`

- for "`a = rv`", if !POCMA, we should require that `value_type` is `MoveInsertable` into `X` and `key_type` and `mapped_type` are `MoveAssignable`

(And we should fix the general container requirements to constrain "`r = rv`", not "`a = rv`".)

Daniel:

The "`a = rv`" problematic is already handled by LWG 3028(i).

[2018-02-13, Priority set to 3 after mailing list discussion]

Proposed resolution:

### 3060(i). `XXX_scan` algorithms are specified to work with move-only `T`, but are specified to make `N` copies of `T` into the destination range

Section: 26.10.8 [exclusive.scan], 26.10.9 [inclusive.scan], 26.10.10 [transform.exclusive.scan], 26.10.11 [transform.inclusive.scan] Status: New Submitter: Billy O'Neal III Opened: 2018-02-06 Last modified: 2019-01-20

Priority: 2

View all other issues in [exclusive.scan].

View all issues with New status.

Discussion:

All of the scan algorithms ([exclusive.scan], [inclusive.scan], [transform.exclusive.scan], [transform.inclusive.scan]) have language like "If `init` is provided, `T` shall be `MoveConstructible` (Table 23); otherwise, `ForwardIterator1`'s value type shall be `MoveConstructible`.". However, the algorithms operational semantics require that that type be written "by copy" to the destination range, making support for move only types impossible.

We probably need to examine real implementations of these things and see what requirements are actually necessary, as in general `GENERALIZED_SUM` and `GENERALIZED_NONCOMMUTATIVE_SUM` need to specify the type used to store intermediate calculations.

[2019-01-20 Reflector prioritization]

Set Priority to 2

Proposed resolution:

### 3063(i). Parallel algorithms in `<memory>` are underspecified

Section: 26.11 [specialized.algorithms] Status: New Submitter: Alisdair Meredith Opened: 2018-02-12 Last modified: 2020-09-06

Priority: 3

View other active issues in [specialized.algorithms].

View all other issues in [specialized.algorithms].

View all issues with New status.

Discussion:

The parallel forms of the uninitialized memory algorithms in `<memory>` are underspecified in two ways. First, they missed the change that all parallel algorithms require at least Forward Iterators, even for input ranges. See P0467R2 for more details.

The second problem is that they do not have a separate specification to the serial forms. This is a problem in two ways. First, there is no more blanket wording saying a parallel policy algorithm has the same semantics as the serial form unless otherwise specified, so in principle these algorithms are totally unspecified. However, assuming that intent, all of the existing specifications use an as-if formulation with code that is explicitly serial in nature, so need a new specification that talks about the effects on each element without including the iteration order.

[2018-02-20, Priority set to 3 after mailing list discussion]

Proposed resolution:

### 3066(i). "report a domain error" in [sf.cmath]/1 is underspecified

Section: 29.7.6 [sf.cmath] Status: New Submitter: Casey Carter Opened: 2018-02-17 Last modified: 2018-06-19

Priority: 3

View all issues with New status.

Discussion:

29.7.6 [sf.cmath]/1 uses the phrase "report a domain error" (emphasis mine):

> If any argument value to any of the functions specified in this subclause is a NaN (Not a Number), the function shall return a NaN but it shall not report a domain error. Otherwise, the function shall report a domain error for just those argument values for which:

> - the function description's Returns: clause explicitly specifies a domain and those argument values fall outside the specified domain, or

> - the corresponding mathematical function value has a nonzero imaginary component, or

> - the corresponding mathematical function is not mathematically defined.

The behavior this phrase is attempting to convey is unclear. A quick search through the text of the standard for "domain error" finds only the `domain_error` exception type defined in 19.2.4 [domain.error]. Is the intent of "report a domain error" that the special math functions throw an exception of type `domain_error`, or is it that they behave as specified in C11 7.12.1 "Treatment of error conditions" para 2 which defines the term "domain error"?

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

### 3069(i). Move assigning `variant`'s subobject corrupts data

Section: 22.6.3.4 [variant.assign] Status: New Submitter: Antony Polukhin Opened: 2018-02-20 Last modified: 2020-09-06

Priority: 3

View other active issues in [variant.assign].

View all other issues in [variant.assign].

View all issues with New status.

Discussion:

`variant::emplace` functions in 22.6.3.5 [variant.mod] destroy the currently contained value before initializing it to a new value. Assignments in 22.6.3.4 [variant.assign] are described in terms on `emplace`.

This leads to situation, when move/copy assigning subobject from `variant` into the same `variant` corrupts data:

>     #include <variant>
>     #include <memory>
>     #include <iostream>

>     using str_t = std::string;
>     using str_ptr_t = std::unique_ptr<str_t>;
>     using var_t = std::variant<str_t, str_ptr_t>;

>     int main()
>     {
>       var_t v = str_ptr_t{
>         new str_t{"Long string that does not fit into SS buffer and forces dynamic allocation"}
>       };

>       // Any of the following lines corrupt the variant's content:
>       v = *std::get<str_ptr_t>(v);
>       //v = std::move(*std::get<str_ptr_t>(v));

>       std::cout << std::get<str_t>(v) << std::endl; // SEGV - 'str_t' inside 'v' is invalid
>     }

Such behavior confuses users, especially those users who are used to `boost::variant`'s behavior. Consider making `variant` assignments safer by defining them close to copy-and-swap.

[2018-06-18 after reflector discussion]

Priority set to 3; Antony volunteered to write a paper for Rapperswil.

Proposed resolution:

This wording is relative to N4727.

1. Change 22.6.3.4 [variant.assign] as indicated:

   >     variant& operator=(const variant& rhs);

   > > -1- Let `j` be `rhs.index()`.

   > > -2- Effects:

   > > 1. (2.1) — If neither `*this` nor `rhs` holds a value, there is no effect.

   > > 2. (2.2) — Otherwise, if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value.

   > > 3. (2.3) — Otherwise, if `index() == j`, assigns the value contained in `rhs` to the value contained in `*this`.

   > > 4. (2.4) — Otherwise, if either `is_nothrow_copy_constructible_v<Tj>` is `true` or `is_nothrow_move_constructible_v<Tj>` is `false`, equivalent to `emplace<j>(get<j>(rhs))`.

   > > 5. (2.5) — Otherwise, equivalent to `emplace<j>(Tj{get<j>(rhs)})``operator=(variant(rhs))`.

   > > […]

   >     variant& operator=(variant&& rhs) noexcept(see below);

   > > -6- Let `j` be `rhs.index()`.

   > > -7- Effects:

   > > 1. (7.1) — If neither `*this` nor `rhs` holds a value, there is no effect.

   > > 2. (7.2) — Otherwise, if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value.

   > > 3. (7.3) — Otherwise, if `index() == j`, assigns `get<j>(std::move(rhs))` to the value contained in `*this`.

   > > 4. (7.4) — Otherwise, equivalent to `emplace<j>(Tj{get<j>(std::move(rhs))})`.

   > > […]

   > > -10- Let `Tj` be a type that is determined as follows: build an imaginary function `FUN(Ti)` for each alternative type `Ti`. The overload `FUN(Tj)` selected by overload resolution for the expression `FUN(std::forward<T>(t))` defines the alternative `Tj` which is the type of the contained value after assignment.

   > > -11- Effects:

   > > 1. (11.1) — If `*this` holds a `Tj`, assigns `std::forward<T>(t)` to the value contained in `*this`.

   > > 2. (11.2) — Otherwise, if `is_nothrow_constructible_v<Tj, T> || !is_nothrow_move_constructible_v<Tj>` is `true`, equivalent to `emplace<j>(std::forward<T>(t))`.

   > > 3. (11.3) — Otherwise, equivalent to `emplace<j>(Tj{std::forward<T>(t)})operator=(variant(std::forward<T>(t)))`.

   > > […]

### 3072(i). [networking.ts] `DynamicBuffer` object lifetimes underspecified

Section: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], 17.6 [networking.ts::buffer.async.read], 17.8 [networking.ts::buffer.async.write], 17.10 [networking.ts::buffer.async.read.until] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-26 Last modified: 2020-09-06

Priority: 3

View other active issues in [networking.ts::buffer.reqmts.dynamicbuffer].

View all other issues in [networking.ts::buffer.reqmts.dynamicbuffer].

View all issues with New status.

Discussion:

Addresses: networking.ts

The `DynamicBuffer` overloads of `async_read` and `async_write`, and `async_read_until`, are underspecified with respect to the lifetime of the dynamic buffer argument `b`.

Asio's implementation (and the intended specification) performs `DECAY_COPY(b)` in the `async_read`, `async_write`, and `async_read_until` initiating functions. All operations performed on `b` are actually performed on that decay-copy, or on a move-constructed descendant of it. The copy is intended to refer to the same underlying storage and be otherwise interchangeable with the original in every way.

Most initiating functions' argument lifetimes are covered by [async.reqmts.async.lifetime]. As an rvalue reference it falls under the second bullet, which specifies that the object is copied (but doesn't say decay-copied).

The proposed resolution adds a postcondition for `DynamicBuffer` move construction, and specifies that `DECAY_COPY(b)` be used for each of these functions. The following two alternative resolutions may also be considered:

- Add an extra bullet to [async.reqmts.async.lifetime] to cover rvalue parameters (but specifically exclude CompletionTokens).

- Change the `DynamicBuffer` arguments to be by-value. (And also change the corresponding synchronous operations to be consistent.)

However, the proposed resolution below is presented as a change that minimizes the scope of the impact.

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4711.

1. Edit 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] as indicated:

   > -3- In Table 14, `x` denotes a value of type `X`, `x1` denotes a (possibly const) value of type `X`, and `mx1` denotes an xvalue of type `X`, `n` denotes a (possibly const) value of type `size_t`, and `u` denotes an identifier.

   > Table 14 — DynamicBuffer requirements
   > expression | type | assertion/note pre/post-conditions
   > --- | --- | ---
   > `X u(mx1);` |  | post: `u.size()` is equal to the prior value of `mx1.size()`. `u.max_size()` is equal to the prior value of `mx1.max_size()`. `u.capacity()` is equal to the prior value of `mx1.capacity()`. `u.data()` satisfies the ConstBufferSequence requirements (16.2.2 [buffer.reqmts.constbuffersequence]) as if copy constructed from the prior value of `mx1.data()`. All valid const or mutable buffer sequences that were previously obtained using `mx1.data()` or `mx1.prepare()` remain valid.

2. Edit 17.6 [networking.ts::buffer.async.read] as indicated:

   > -11- Let `bd` be the result of `DECAY_COPY(b)`. Data is placed into the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object `bbd`. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to each `read_some` call using `bd.prepare(N)`, where `N` is an unspecified value less than or equal to `bd.max_size() - bd.size()`. [Note: Implementations are encouraged to use `bd.capacity()` when determining `N`, to minimize the number of `read_some` calls performed on the stream. -- end note] After each `read_some` call, the implementation performs `bd.commit(n)`, where `n` is the return value from `read_some`.

   > […]

   > -13- The synchronous read operation continues until:

   > - `bd.size() == bd.max_size()`; or

   > - the completion condition returns `0`.

3. Edit 17.8 [networking.ts::buffer.async.write] as indicated:

   > -11- Let `bd` be the result of `DECAY_COPY(b)`. Data is written from the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object `bd`. A constant buffer sequence (16.2.2 [buffer.reqmts.constbuffersequence]) is obtained using `bd.data()`. After the data has been written to the stream, the implementation performs `bd.consume(n)`, where `n` is the number of bytes successfully written.

   > […]

   > -13- The asynchronous write operation continues until:

   > - `bd.size() == 0`; or

   > - the completion condition returns `0`.

4. Edit 17.10 [networking.ts::buffer.async.read.until] as indicated:

   > -3- Effects: Let `bd` be the result of `DECAY_COPY(b)`. Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream (17.1.2 [buffer.stream.reqmts.asyncreadstream]) object `stream` by performing zero or more asynchronous read_some operations on the stream, until the readable bytes of the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object `bd` contain the specified delimiter `delim`.

   > -4- Data is placed into the dynamic buffer object `bd`. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to each `async_read_some` call using `bd.prepare(N)`, where `N` is an unspecified value such that `N <= max_size() - size()`. [Note: Implementations are encouraged to use `bd.capacity()` when determining `N`, to minimize the number of asynchronous read_some operations performed on the stream. — end note] After the completion of each asynchronous `read_some` operation, the implementation performs `bd.commit(n)`, where `n` is the value passed to the asynchronous `read_some` operation's completion handler.

   > -5- The asynchronous `read_until` operation continues until:

   > - the readable bytes of `bd` contain the delimiter `delim`; or

   > - `bd.size() == bd.max_size()`; or

   > - an asynchronous `read_some` operation fails.

   > […]

   > -8- On completion of the asynchronous operation, if the readable bytes of `bd` contain the delimiter, `ec` is set such that `!ec` is `true`. Otherwise, if `bd.size() == bd.max_size()`, `ec` is set such that `ec == stream_errc::not_found`. If `bd.size() < bd.max_size()`, `ec` is the `error_code` from the most recent asynchronous `read_some` operation. `n` is the number of readable bytes in `bd` up to and including the delimiter, if present, otherwise `0`.

### 3073(i). [networking.ts] (`async_`)`read` and (`async_`)`write` don't support `DynamicBuffer` lvalues

Section: 17 [networking.ts::buffer.stream] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-27 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

Addresses: networking.ts

Suppose that we have a user-defined class `dyn_buf` that satisfies the DynamicBuffer requirements ([buffer.reqmts.dynamicbuffer]), and is additionally copy-constructible. The following snippet compiles, as expected:

>     dyn_buf b;
>     net::read_until(my_socket, b, "\n");

However, this next snippet will not compile, when it should:

>     dyn_buf b;
>     net::read(my_socket, b);

This is due to:

- the `DynamicBuffer` template parameter being deduced as `dyn_buf&`, and
- the `read` overload being specified to not participate in overload resolution unless `is_dynamic_buffer_v<DynamicBuffer>` is `true`.

This can fixed by changing the test to `is_dynamic_buffer_v<decay_t<DynamicBuffer>>`.

[2019-01-20 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4711.

1. Edit 17.5 [networking.ts::buffer.read] as indicated:

   > -14- Remarks: This function shall not participate in overload resolution unless `is_dynamic_buffer_v<decay_t<DynamicBuffer>>` is `true`.

2. Edit 17.6 [networking.ts::buffer.async.read] as indicated:

   > -16- Remarks: This function shall not participate in overload resolution unless `is_dynamic_buffer_v<decay_t<DynamicBuffer>>` is `true`.

3. Edit 17.7 [networking.ts::buffer.write] as indicated:

   > -14- Remarks: This function shall not participate in overload resolution unless `is_dynamic_buffer_v<decay_t<DynamicBuffer>>` is `true`.

4. Edit 17.8 [networking.ts::buffer.async.write] as indicated:

   > -16- Remarks: This function shall not participate in overload resolution unless `is_dynamic_buffer_v<decay_t<DynamicBuffer>>` is `true`.

### 3078(i). `directory_entry`, `directory_iterator` and `recursive_directory_iterator` perform needless path copies

Section: 31.12.10 [fs.class.directory.entry], 31.12.11 [fs.class.directory.iterator], 31.12.12 [fs.class.rec.dir.itr] Status: New Submitter: Gor Nishanov Opened: 2018-03-05 Last modified: 2019-04-02

Priority: 3

View all other issues in [fs.class.directory.entry].

View all issues with New status.

Discussion:

An implementation of `directory_entry` class is likely to store a `filesystem::path` as a member. Constructors and assign member functions take `filesystem::path` by `const&` thus forcing creation of a copy.

An implementation of `directory_iterator` class is likely to store a `directory_entry` or a `path` as a part of its state. Constructors take `filesystem::path` by `const&` thus forcing creation of a copy.

An implementation of `recursive_directory_iterator` class is likely to store a `directory_entry` or a `path` as a part of its state. Constructors take `filesystem::path` by `const&` thus forcing creation of a copy.

Suggested resolution:

Add overloads to `directory_entry`, `directory_iterator`, and `recursive_directory_iterator` that take `filesystem::path` by `&&`.

Make it unspecified in case an exception is thrown from those new members where an argument was moved from or not.

>     explicit directory_entry(const filesystem::path& p);
>     explicit directory_entry(filesystem::path&& p);
>     directory_entry(const filesystem::path& p, error_code& ec);
>     directory_entry(filesystem::path&& p, error_code& ec);

>     void directory_entry::assign(const filesystem::path& p);
>     void directory_entry::assign(filesystem::path&& p);
>     void directory_entry::assign(const filesystem::path& p, error_code& ec);
>     void directory_entry::assign(filesystem::path&& p, error_code& ec);

>     explicit directory_iterator(const path& p);
>     explicit directory_iterator(path&& p);
>     directory_iterator(const path& p, directory_options options);
>     directory_iterator(path&& p, directory_options options);
>     directory_iterator(const path& p, error_code& ec) noexcept;
>     directory_iterator(path&& p, error_code& ec) noexcept;
>     directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>     directory_iterator(path&& p, directory_options options, error_code& ec) noexcept;

>     explicit recursive_directory_iterator(const path& p);
>     explicit recursive_directory_iterator(path&& p);
>     recursive_directory_iterator(const path& p, directory_options options);
>     recursive_directory_iterator(path&& p, directory_options options);
>     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>     recursive_directory_iterator(path&& p, directory_options options, error_code& ec) noexcept;
>     recursive_directory_iterator(const path& p, error_code& ec) noexcept;
>     recursive_directory_iterator(path&& p, error_code& ec) noexcept;

[2018-03-20 Priority set to 3 after discussion on the reflector.]

Proposed resolution:

### 3081(i). Floating point `from_chars` API does not distinguish between overflow and underflow

Section: 28.2.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2026-04-07

Priority: 2

View other active issues in [charconv.from.chars].

View all other issues in [charconv.from.chars].

View all issues with Open status.

Discussion:

`strtod()` distinguishes between overflow and underflow by returning a value that is either very large or very small. Floating point `from_chars` does not currently offer any way for callers to distinguish these two cases.

It would be beneficial if users could migrate from `strtod()` to `from_chars` without loss of functionality.

I recommend that floating point `from_chars` use `value` as an overflow-vs-underflow reporting channel, in the same manner as `strtod()`.

My proposed wording gives `from_chars` the same wide latitude that `strtod()` enjoys for handling underflow. A high-quality implementation would likely set `ec == result_out_of_range` for underflow only when the nearest representable `float` to the parsed value is a zero and the parsed mantissa was nonzero. In this case `value` would be set to (an appropriately-signed) zero. It is worth considering giving `from_chars` this more predictable behavior, if library writers feel they can provide this guarantee for all platforms. (I have a proof-of-concept integer-based implementation for IEEE doubles with this property.)

[2018-06 Rapperswil Wednesday issues processing]

Marshall to provide updated wording and propose Tentatively Ready on the reflector.

Priority set to 2

[2018-08-23 Batavia Issues processing]

Status to Open; Marshall to reword

[2023-03-29; Jonathan adds further discussion]

There are conflicting interpretations of "not in the range representable" for floating-point types. One view is that 1e-10000 and 1e+10000 are outside the representable range for a 64-bit double-precision `double` (which has min/max exponents of -1022 and 1023). Another view is that the representable range for floating-point types is [-inf,+inf], which means that there are values that cannot be accurately represented, but there are no values "not in the range representable". And 1e-10000 is clearly within the range [0,`numeric_limits<double>::max()`], even if we don't use infinity as the upper bound of the range. Under the second interpretation, the result will be ±0.0 for underflow and ±inf for overflow, but `ec` will not be set.

The current proposed resolution does address this, by making it clear that `value` should be set to a very small or very large value (with appropriate sign), but that `ec` should also be set. The use of the word "overflow" for the integer overloads is a problem though, because the result cannot "overflow" an unsigned integer type, but can certainly be outside its range.

[2025-12-10; P2827R1 addresses this]

[2026-04-07; P4168R0 addresses this]

Proposed resolution:

This wording is relative to N4727.

1. Edit 28.2.3 [charconv.from.chars] as indicated:

   > […] Otherwise, the characters matching the pattern are interpreted as a representation of a value of the type of `value`. The member `ptr` of the return value points to the first character not matching the pattern, or has the value `last` if all characters match. If the parsed value is not in the range representable by the type of `value`, `value` is unmodified and the member `ec` of the return value is equal to `errc::result_out_of_range`. Otherwise, `value` is set to the parsed value, after rounding according to `round_to_nearest` (17.3.4 [round.style]), and the member `ec` is value-initialized.

   >     from_chars_result from_chars(const char* first, const char* last,
   >                                  see below& value, int base = 10);

   > > -2- Requires: `base` has a value between 2 and 36 (inclusive).

   > > -3- Effects: The pattern is the expected form of the subject sequence in the `"C"` locale for the given nonzero base, as described for `strtol`, except that no `"0x"` or `"0X"` prefix shall appear if the value of `base` is 16, and except that `'-'` is the only sign that may appear, and only if `value` has a signed type. On overflow, `value` is unmodified.

   > > […]

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Requires: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the `"C"` locale, as described for `strtod`, except that

   > > 1. (7.1) […]

   > > 2. (7.2) […]

   > > 3. (7.3) […]

   > > 4. (7.4) […]

   > > In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern. On overflow, `value` is set to plus or minus `std::numeric_limits<T>::max()` of the appropriate type. On underflow, `value` is set to a value with magnitude no greater than `std::numeric_limits<T>::min()`.

   > > […]

### 3082(i). `from_chars` specification regarding floating point rounding is inconsistent

Section: 28.2.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2026-04-07

Priority: 2

View other active issues in [charconv.from.chars].

View all other issues in [charconv.from.chars].

View all issues with Open status.

Discussion:

P0682R1 added the requirement that `from_chars` use `round_to_nearest` when converting from string, but later text in the section suggests that the implementation has latitude in its choice of rounding logic.

If the intent is merely that the floating point environment should not affect `from_chars` behavior, the rounding-mode text should be weakened. If the intent is to always require `round_to_nearest`, the text suggesting a latitude in rounding logic should be removed.

[2018-03-27 Priority set to 2 after discussion on the reflector.]

[2018-06 Rapperswil Wednesday issues processing]

Status to open; also this needs to say that the intent is to be independent of the floating point environment.

[2018-08-23 Batavia Issues processing]

Marshall to talk to Jens about this

[2024-12-04; add comments from Richard Smith]

In editorial issue #6730 Richard said:

> 28.2.3 [charconv.from.chars]/6.4 says:

> > In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern.

> This is ambiguous. It could mean either:
> 1. The resulting value is the implementation's choice of one of a set of values, and that set contains the two values closest to the value of the string.
> 2. The resulting value is the implementation's choice of one of a set of values, and that set contains all values that are closest to the value of the string (of which it turns out there can be up to two).

> I think the normal English interpretation would be (1), but the intended interpretation is actually (2).

> (Under (1), the string `"1.0"` can produce the value one ULP less than 1.0 or it can produce 1.0, and the string `"1.00<lots of 0s>1"` can produce those same two values, because the value one ULP less than 1.0 is closer to that string than the value one ULP greater than 1.0.)

> Perhaps the wording from 7.3.10 [conv.double]/2 and 7.6.1.9 [expr.static.cast]/11 can be used instead:

> > If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values.

Editorial pull request #6833 proposed a change along those lines, but conflicts with the proposed resolution to this issue. We should address Richard's comment as part of this issue.

[2026-04-07; P4168R0 addresses this]

Proposed resolution:

This wording is relative to N4727.

1. Edit 28.2.3 [charconv.from.chars] as indicated:

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Requires: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the `"C"` locale, as described for `strtod`, except that

   > > 1. (7.1) […]

   > > 2. (7.2) […]

   > > 3. (7.3) […]

   > > 4. (7.4) […]

   > > In any case, the resulting value is one of at most twothe floating-point values closest to the value of the string matching the pattern, with ties broken according to `round_to_nearest`.

   > > […]

### 3084(i). Termination in C++ is unclear

Section: 17.5 [support.start.term], 17.9.5 [exception.terminate] Status: New Submitter: JF Bastien Opened: 2018-03-15 Last modified: 2024-07-26

Priority: 3

View other active issues in [support.start.term].

View all other issues in [support.start.term].

View all issues with New status.

Discussion:

It's unclear how different termination facilities in C++ interact (and how they interact with the C termination facilities). Individually some of these functions try to handle corner cases, but hilarity ensues when combined with each other. As a simple example, can an `atexit` handler call `exit`? If not, can it call `quick_exit`, and can then `at_quick_exit` handler call `exit`? Is it possible to install an `atexit` handler from an `at_quick_exit`, without strongly happens before, while handling a separate `atexit` handler (and what happens then)?

The termination handlers and termination conditions I collected:

- returning from `main` calls `atexit` handlers.

- `atexit` / `exit`

- `at_quick_exit` / `quick_exit`

- `set_terminate`

- violating `noexcept` and other things that call `std::terminate` (see `[except.terminate]`)

- violating exception specification

- parallel algorithms leaving with uncaught exception

- some `std::signal` such as `SIGTERM`, `SIGSEGV`, `SIGINT`, `SIGILL`, `SIGABRT`, and (maybe?) `SIGFPE`.

- `set_unexpected` (now a zombie)

- `unexpected_handler` (now a zombie)

What's unclear is:

- Is termination handling a DAG?

- Which thread(s) are termination handlers called on?

- Is program termination Turing complete?

I've written a sample program which exercises some of this, see here.

[2018-04-02, Jens comments]

Any potential wording should carefully take [basic.start] into account, and maybe should actually be integrated into the core wording, not the library wording.

[2018-04-02 Priority set to 3 after discussion on the reflector.]

[2024-07-26; Jonathan comments]

In C89 and C99 the spec for `exit` in C said "If more than one call to the exit function is executed by a program, the behavior is undefined." Since C11 that was updated to also talk about `at_quick_exit`, saying "If a program calls the `exit` function more than once, or calls the `quick_exit` function in addition to the `exit` function, the behavior is undefined." The spec for `quick_exit` is similar.

That answers most of the questions here. An `atexit` or `at_quick_exit` handler cannot call `exit` or `quick_exit`, because if a handler is running then it means that `exit` or `quick_exit` has already been called, and calling either of them again would be undefined. It doesn't matter whether an `atexit` handler installs an `at_quick_exit` handler, because once `exit` handlers start running it would be undefined to call `quick_exit`, and vice versa. So you should never have a situation where both sets of handlers are running.

There is a suggestion to relax this in POSIX so that calling `exit` or `quick_exit` again from other threads would not be UB but would just block until the process exits, which should happen eventually assuming exit handlers make forward progress (calling `exit` or `quick_exit` from a handler would still be UB).

Why does C++ not make it undefined to call `exit` twice? Can we change that?

Proposed resolution:

### 3086(i). Possible problem in §[new.delete.single]

Section: 17.6.3.2 [new.delete.single] Status: New Submitter: William M. Miller Opened: 2018-03-16 Last modified: 2020-09-06

Priority: 3

View all other issues in [new.delete.single].

View all issues with New status.

Discussion:

In general requirements on a whole program, as opposed to a single translation unit, generally specify "no diagnostic required", since we don't want to require implementations to do multi-translation-unit analysis. However, 17.6.3.2 [new.delete.single] paragraph 11 says,

> If a function with a `size` parameter is defined, the program shall also define the corresponding version without the `size` parameter.

This is clearly not restricted to a single translation unit; should "no diagnostic required" be added?

[2018-04-03; Thomas Köppe and Tim Song suggest wording]

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4727.

1. Edit 17.6.3.2 [new.delete.single] as indicated:

   >     void operator delete(void* ptr) noexcept;
   >     void operator delete(void* ptr, std::size_t size) noexcept;
   >     void operator delete(void* ptr, std::align_val_t alignment) noexcept;
   >     void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept;

   > > -10- Effects: […]

   > > -11- Replaceable: A C++ program may define functions with any of these function signatures, and thereby displace the default versions defined by the C++ standard library. If a function without a `size` parameter is defined, the program should also define the corresponding function with a `size` parameter. If a function with a `size` parameter is defined, the program shall also define the corresponding version without the `size` parameter; no diagnostic is required. [Note: The default behavior below may change in the future, which will require replacing both deallocation functions when replacing the allocation function. — end note]

2. Edit 17.6.3.3 [new.delete.array] as indicated:

   >     void operator delete[](void* ptr) noexcept;
   >     void operator delete[](void* ptr, std::size_t size) noexcept;
   >     void operator delete[](void* ptr, std::align_val_t alignment) noexcept;
   >     void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept;

   > > -9- Effects: […]

   > > -10- Replaceable: A C++ program may define functions with any of these function signatures, and thereby displace the default versions defined by the C++ standard library. If a function without a `size` parameter is defined, the program should also define the corresponding function with a `size` parameter. If a function with a `size` parameter is defined, the program shall also define the corresponding version without the `size` parameter; no diagnostic is required. [Note: The default behavior below may change in the future, which will require replacing both deallocation functions when replacing the allocation function. — end note]

### 3089(i). `copy_n` should require non-overlapping ranges

Section: 26.7.1 [alg.copy] Status: New Submitter: Marshall Clow Opened: 2018-03-21 Last modified: 2022-11-06

Priority: 3

View other active issues in [alg.copy].

View all other issues in [alg.copy].

View all issues with New status.

Discussion:

All the copy algorithms have some kind of prohibition on having the input and output ranges overlap.

The serial version of `copy` says:

> Requires: `result` shall not be in the range `[first, last)`.

The parallel version of `copy` says:

> Requires: The ranges `[first, last)` and `[result, result + (last - first))` shall not overlap.

`copy_if` says:

> Requires: The ranges `[first, last)` and `[result, result + (last - first))` shall not overlap.

`copy_backwards` says:

> Requires: `result` shall not be in the range `[first, last)`.

But `copy_n` has no such requirement.

I think it should. I checked the minutes of the LWG discussion from 2008 when this was added, and there was no discussion of overlapping ranges.

What formulation do we want here? Is it sufficient to say "... shall not be in the range ..." or should we use the stronger "... shall not overlap ..."? Some `copy` variants use one, some use the other. Should we be consistent? Issue 3085(i) is a similar issue for `char_traits::copy`.

[2018-06-18 after reflector discussion]

Priority set to 3

Previous resolution [SUPERSEDED]:

> This wording is relative to N4727.

> 1. Edit 26.7.1 [alg.copy] as indicated:

>    > [Drafting note: I'm using the permission in 26.2 [algorithms.requirements]/10 to do random-access arithmetic on (possibly) input iterators.]

>    >     template<class InputIterator, class Size, class OutputIterator>
>    >       constexpr OutputIterator copy_n(InputIterator first, Size n,
>    >                                       OutputIterator result);
>    >     template<class ExecutionPolicy, class ForwardIterator1, class Size, class ForwardIterator2>
>    >       ForwardIterator2 copy_n(ExecutionPolicy&& exec,
>    >                               ForwardIterator1 first, Size n,
>    >                               ForwardIterator2 result);

>    > > -?- Requires: `result` shall not be in the range `[first, first + n)`.

>    > > -9- Effects: For each non-negative integer `i < n`, performs `*(result + i) = *(first + i)`.

>    > > -10- Returns: `result + n`.

>    > > -11- Complexity: Exactly `n` assignments.

[2022-11-06; Daniel syncs wording with recent working draft]

Proposed resolution:

This wording is relative to N4917.

1. Edit 26.7.1 [alg.copy] as indicated:

   > [Drafting note: I'm using the permission in 26.2 [algorithms.requirements]/10 to do random-access arithmetic on (possibly) input iterators.]

   >     template<class InputIterator, class Size, class OutputIterator>
   >       constexpr OutputIterator copy_n(InputIterator first, Size n,
   >                                       OutputIterator result);
   >     template<class ExecutionPolicy, class ForwardIterator1, class Size, class ForwardIterator2>
   >       ForwardIterator2 copy_n(ExecutionPolicy&& exec,
   >                               ForwardIterator1 first, Size n,
   >                               ForwardIterator2 result);
   >     template<input_iterator I, weakly_incrementable O>
   >       requires indirectly_copyable<I, O>
   >       constexpr ranges::copy_n_result<I, O>
   >         ranges::copy_n(I first, iter_difference_t<I> n, O result);

   > > -10- Let `N` be max`(0, n)`.

   > > -11- Mandates: The type `Size` is convertible to an integral type (7.3.9, 11.4.8).

   > > -?- Preconditions: `result` is not in the range `[first, first + n)`.

   > > -12- Effects: For each non-negative integer `i < N`, performs `*(result + i) = *(first + i)`.

   > > -13- Returns:

   > > 1. (13.1) — `result + N` for the overloads in namespace `std`.

   > > 2. (13.2) — `{first + N, result + N}` for the overload in namespace `ranges`.

   > > -14- Complexity: Exactly `N` assignments.

### 3092(i). Unclear semantics of `enum class` bitmask types

Section: 16.3.3.3.3 [bitmask.types] Status: Open Submitter: Geoffrey Romer Opened: 2018-03-26 Last modified: 2020-09-06

Priority: 3

View other active issues in [bitmask.types].

View all other issues in [bitmask.types].

View all issues with Open status.

Discussion:

[bitmask.types] specifies the semantics of a bitmask type in terms of an "exposition only" `enum` definition, together with some constants and overloads. Notably, it is depicted as an unscoped enum, which implies among other things that it is implicitly convertible to `int_type`. At least some sources treat that as normative (as of this writing, cppreference.com's documentation for `BitmaskType` says the expression `(X & Y) != 0` is guaranteed to be well-formed), and it's hard to argue that they're wrong on the basis of the existing wording.

On the other hand, many standard library types are depicted as scoped enums, but still specified to be "bitmask types". As far as I can tell, it's impossible in library code to make a scoped enum implicitly convertible to its underlying type, and even if you could, what would be the point? Presumably the specification of those types as scoped enums is intended to have some sort of observable consequences.

In addition, some library types (notably in clause 31) are specified to be bitmask types, without indicating whether they are scoped or unscoped. It's not clear what the standard guarantees about e.g. whether they can be implicitly converted.

I assume the intent is that "bitmask type" doesn't specify an implicit conversion, or any of the other ways unscoped and scoped enums differ, but the standard doesn't actually say that. We really ought to rewrite [bitmask.types] as a requirements table, but here's a quick hack to the existing wording as a stopgap.

[2018-04-23 Priority set to 2 after discussion on the reflector.]

[2018-08-23 Batavia Issues processing]

N3110 also touches on this.

Nico to survey the enums in the library and report back on which ones should be class.

[2019 Cologne Wednesday night]

Changing existing enums to class enums is an ABI break on some platforms; current wording does not require the use of enums. See N3110.

Daniel to provide requirements tables, Jonathan to assist. Reduce priority to 3

Proposed resolution:

This wording is relative to N4727.

1. Edit 16.3.3.3.3 [bitmask.types] as indicated:

   > -2- The bitmask type `bitmask` can be written:

   > >     // For exposition only.
   > >     // int_type is an integral type capable of representing all values of the bitmask type.
   > >     enumE bitmask : int_type {
   > >       V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, .....
   > >     };
   > >     […]

   > -3- Here, `E` may represent either `enum` or `enum class` (the choice is implementation-defined unless otherwise specified), and the names `C0`, `C1`, etc. represent bitmask elements for this particular bitmask type. The zero value `bitmask{}` is used to represent an empty bitmask, in which no bitmask elements are set. All suchbitmask elements have distinct, nonzero values such that, for any pair `Ci` and `Cj` where `i ≠ j`, `Ci & Ci` is nonzero and `Ci & Cj` is zero. Additionally, the value `0` is used to represent an empty bitmask, in which no bitmask elements are set.

### 3093(i). LWG 2294/2192 missed a `std::abs` overload

Section: 29.7.2 [c.math.abs], 17.2.2 [cstdlib.syn], 31.13.2 [cinttypes.syn] Status: New Submitter: Richard Smith Opened: 2018-03-30 Last modified: 2018-06-19

Priority: 3

View all issues with New status.

Discussion:

LWG 2192(i) changed the library specification so that you always get all the `std::abs` overloads for fundamental types if you get any of them.

Except that it missed one: `<cinttypes>` provides a `std::abs` (and `std::div`) overload for `intmax_t` if it's not `long long` (since LWG 1449(i)). Presumably that one should also follow the `<cstdlib>`/`<cmath>` pattern and we should make the complete `abs` overload set available whenever any of the three headers is included? (And likewise we should make the complete `std::div` overload set available whenever either `<cstdlib>` or `<cinttypes>` is included.)

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

### 3097(i). `basic_stringbuf seekoff` effects trigger undefined behavior and have contradictory returns

Section: 31.8.2.5 [stringbuf.virtuals] Status: New Submitter: Billy O'Neal III Opened: 2018-04-07 Last modified: 2020-09-06

Priority: 3

View other active issues in [stringbuf.virtuals].

View all other issues in [stringbuf.virtuals].

View all issues with New status.

Discussion:

Paragraph citations relative to N4727.

[stringbuf.virtuals]/10 says that `newoff` might be calculated from `xnext - xbegin`, or from `high_mark - xbegin`. After `newoff` is calculated, it does the null pointer check against and zero offset check. However, that means the effects may have already done `nullptr -` non-`nullptr`, or non-`nullptr - nullptr`, which [expr.add]/5 says is undefined behavior.

Moreover, the attempt at avoiding this problem only tests `newoff`, not the value actually used which is `newoff + off`. For example, `buf.seekoff(100, ios_base::beg, ios_base::out)` on a read-only `streambuf` would try to assign `pptr() + newoff + off to pptr()`, but `pptr()` may have been `nullptr`, giving `nullptr + 0 + 100` which triggers UB. (Perhaps the "refers to an uninitialized character" bit protects that though).

Last, the Returns: element says that it returns `newoff`, but then also says it returns the resulting stream position, which should be something like `newoff + off`. (I checked libc++ and MSVC++ and we both return `newoff + off`)

We probably want to resolve that by renaming the value that comes out of Table 108 to something like "`basis`" and make "`newoff`" actually be the new offset instead of the starting offset.

[2018-04-16 Priority set to 3 after discussion on the reflector.]

Proposed resolution:

### 3098(i). Misleading example for `filesystem::path::filename()`

Section: 31.12.6.5.9 [fs.path.decompose] Status: New Submitter: Jonathan Wakely Opened: 2018-04-06 Last modified: 2020-09-06

Priority: 3

View all other issues in [fs.path.decompose].

View all issues with New status.

Discussion:

The example in [fs.path.decompose] p7 includes:

>     path("//host").filename();  // yields ""

This result isn't guaranteed, it depends whether the implementation interprets `"//host"` as a root-name or as a root-directory (with a redundant directory-separator) followed by the filename `"host"`.

The example should make it clear that this interpretation is allowed.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4727.

> 1. Edit 31.12.6.5.9 [fs.path.decompose] as indicated:

>    >     path filename() const;

>    > > -6- Returns: `relative_path().empty() ? path() : *--end()`.

>    > > [Example:

>    > > >     path("/foo/bar.txt").filename(); // yields "bar.txt"
>    > > >     path("/foo/bar").filename();     // yields "bar"
>    > > >     path("/foo/bar/").filename();    // yields ""
>    > > >     path("/").filename();            // yields ""
>    > > >     path("//host").filename();       // yields "" or "host"
>    > > >     path(".").filename();            // yields "."
>    > > >     path("..").filename();           // yields ".."

>    > > — end example]

[2018-04-10, Jonathan comments and provides revised wording]

Based on the reflector discussion I'd like to change the P/R to Billy's suggestion of simply removing that line from the example.

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4741.

1. Edit 31.12.6.5.9 [fs.path.decompose] as indicated:

   >     path filename() const;

   > > -6- Returns: `relative_path().empty() ? path() : *--end()`.

   > > [Example:

   > > >     path("/foo/bar.txt").filename(); // yields "bar.txt"
   > > >     path("/foo/bar").filename();     // yields "bar"
   > > >     path("/foo/bar/").filename();    // yields ""
   > > >     path("/").filename();            // yields ""
   > > >     path("//host").filename();       // yields ""
   > > >     path(".").filename();            // yields "."
   > > >     path("..").filename();           // yields ".."

   > > — end example]

### 3099(i). `is_assignable<Incomplete&, Incomplete&>`

Section: 21.3.3 [meta.type.synop] Status: Open Submitter: Casey Carter Opened: 2018-04-10 Last modified: 2025-10-27

Priority: 2

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with Open status.

Discussion:

Addresses RU-147

LWG 2939(i) suggests that the the preconditions of the type traits need reevaluation. This issue focuses specifically on `is_assignable` and, by extension, its variants:

- `is_copy_assignable<T>`, equivalent to `is_assignable<T&, const T&>`,
- `is_move_assignable<T>`, equivalent to `is_assignable<T&, T>`,
- `is_trivially_assignable<T, U>`, equivalent to `is_assignable<T, U> && /* magic */`,
- `is_trivially_copy_assignable<T>`, equivalent to `is_assignable<T&, const T&> && /* magic */`,
- `is_trivially_move_assignable<T>`, equivalent to `is_assignable<T&, T> && /* magic */`,
- `is_nothrow_assignable<T, U>`, equivalent to `is_assignable<T, U> && noexcept(declval<T>() = declval<U>())`,
- `is_nothrow_copy_assignable<T>`, equivalent to `is_assignable<T&, const T&> && noexcept(declval<T&>() = declval<const T&>())`,
- `is_nothrow_move_assignable<T>`, equivalent to `is_assignable<T&, T> && noexcept(declval<T&>() = declval<T>())`, and

We note a discrepancy: `is_copy_assignable<T>` requires `T` to be a complete type, but the equivalent form `is_assignable<T&, const T&>` does not. The requirement for `is_copy_assignable<T>` seems sensible, since there's no way to determine whether or not the assignment `declval<T&>() = declval<const T&>()` is well-formed when `T` is incomplete. It seems that the same argument should apply to all of the above "assignable" traits, and that they must require that the referent type is complete when given a reference type parameter to be implementable.

[2018-08 Batavia Monday issue discussion]

Issues 2797(i), 2939(i), 3022(i), and 3099(i) are all closely related. Walter to write a paper resolving them.

[2020-02-14, Prague]

LWG discussions. Set priority to 2.

[2023-06-12; Varna]

P1285R0 is related to this issue.

Previous resolution [SUPERSEDED]

This wording is relative to N4741.

> 1. In 21.3.5.4 [meta.unary.prop] Table 42, change the Precondition text for `is_assignable`, `is_trivially_assignable`, and `is_nothrow_assignable` as follows:

>    > `remove_cvref_t<``T``>` and `remove_cvref_t<``U``>` shall be complete types, cv `void`, or arrays of unknown bound.

> 2. In 21.3.5.4 [meta.unary.prop] Table 42, change the Precondition text for `is_copy_assignable`, `is_move_assignable`, `is_trivially_copy_assignable`, `is_trivially_move_assignable`, `is_nothrow_copy_assignable`, and `is_nothrow_move_assignable` as follows:

>    > `remove_cvref_t<``T``>` shall be a complete type, cv `void`, or an array of unknown bound.

[2024-08-21; Jonathan provides improved wording]

During LWG telecon review Tomasz pointed out that we don't always require a complete type for the right operand of an assignment. Given `T::operator=(U&)` we should be able to give a correct answer for `is_assignable<T&, U&>` whether of not `U` is complete. This also affects e.g. `is_constructible<T, U&>` if `T::T(U&)` exists.

So for the examples above, `remove_cvref_t<U>` is not needed to give a correct answer. However, if `T::operator=(U&)` does not exist, then we do need `U` to be complete so we can tell if there is an implicit conversion sequence to `T` or another type that can be assigned to `T`. We do not know how to solve this problem, and it's broader than just `is_assignable`. It was suggested to make an incremental improvement to `is_assignable` and open a new issue for the broader issue.

Proposed resolution:

This wording is relative to N4988.

> 1. In 21.3.5.4 [meta.unary.prop] Table 51, change the Precondition text for `is_assignable`, `is_trivially_assignable`, and `is_nothrow_assignable` as follows:

>    > `remove_cvref_t<``T``>` and `U` shall be complete types, cv `void`, or arrays of unknown bound.

> 2. In 21.3.5.4 [meta.unary.prop] Table 51, change the Precondition text for `is_copy_assignable`, `is_move_assignable`, `is_trivially_copy_assignable`, `is_trivially_move_assignable`, `is_nothrow_copy_assignable`, and `is_nothrow_move_assignable` as follows:

>    > `remove_cvref_t<``T``>` shall be a complete type, cv `void`, or an array of unknown bound.

### 3105(i). `T1` is convertible to `T2`

Section: 16 [library] Status: New Submitter: Jens Maurer Opened: 2018-04-24 Last modified: 2023-06-25

Priority: 3

View other active issues in [library].

View all other issues in [library].

View all issues with New status.

Discussion:

The library wording frequently uses the construction "type `T1` is convertible to type `T2`", but this is an undefined phrase.

For requirements on user code (e.g. [tuple.rel]), it is unclear whether all expressions of type `T1` must satisfy the convertibility requirement, regardless of value category, or whether a single value category is in view only.

Consider:

>     struct C
>     {
>       operator int() &&;
>     };

>     int main()
>     {
>       int x = C(); // prvalue can be implicitly converted to int
>       C c;
>       int y = c;   // lvalue can't
>     }

The library has an "`is_convertible<T1, T2>`" trait, but that checks convertibility only for a single value category, not all possible ones.

[2018-06-18 after reflector discussion]

Priority set to 3

[2023-06-24; Daniel comments]

This issue has very much overlap with LWG 484(i).

Proposed resolution:

### 3107(i). `istreambuf_iterator` has public exposition-only member

Section: 24.6.4 [istreambuf.iterator] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2020-09-06

Priority: 4

View other active issues in [istreambuf.iterator].

View all other issues in [istreambuf.iterator].

View all issues with New status.

Discussion:

LWG has said recently that we don't want public exposition-only things, as that may encourage users to try to use those names (and some implementers to actually use those names).

[2018-06-18 after reflector discussion]

Priority set to 4

Proposed resolution:

This wording is relative to N4741.

1. Edit 24.6.4 [istreambuf.iterator] as indicated:

   >     namespace std {
   >       template<class charT, class traits = char_traits<charT>>
   >       class istreambuf_iterator {
   >       public:
   >         […]
   >         using istream_type = basic_istream<charT,traits>;

   >         class proxy; // exposition only

   >         constexpr istreambuf_iterator() noexcept;
   >         […]
   >       private:
   >         class proxy; // exposition only
   >         streambuf_type* sbuf_; // exposition only
   >       };
   >       […]
   >     }

### 3108(i). `istreambuf_iterator::proxy::operator*` should be `const`

Section: 24.6.4.2 [istreambuf.iterator.proxy] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

`operator*` on iterators is usually intended to be `const`; see 24.3.5.3 [input.iterators] Table 87, `*a`, where `a` is of type `X` or `const X`. (Technically, `proxy` is implementing the `*r++` requirement in this table, and `r` doesn't imply a const iterator, but there's no reason for the iterator's `operator*` to differ from the proxy)

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4741.

1. Edit 24.6.4.2 [istreambuf.iterator.proxy] as indicated:

   >     namespace std {
   >       template<class charT, class traits = char_traits<charT>>
   >       class istreambuf_iterator<charT, traits>::proxy { // exposition only
   >         charT keep_;
   >         basic_streambuf<charT,traits>* sbuf_;
   >         proxy(charT c, basic_streambuf<charT,traits>* sbuf)
   >           : keep_(c), sbuf_(sbuf) { }
   >         public:
   >           charT operator*() const { return keep_; }
   >       };
   >     }

### 3114(i). [networking.ts] Permit efficient composition when using `DynamicBuffer`

Section: 16 [networking.ts::buffer], 17 [networking.ts::buffer.stream] Status: LEWG Submitter: Vinnie Falco Opened: 2018-05-18 Last modified: 2025-10-21

Priority: 4

View all issues with LEWG status.

Discussion:

Addresses: networking.ts

Asynchronous algorithms are started by a call to an initiating function. When these algorithms are constructed from calls to other initiating functions, the result is called a composed operation. For example, `async_read` may be implemented in terms of zero or more calls to a stream's `async_read_some` algorithm. For operations where the caller cannot easily determine ahead of time the storage requirements needed for an algorithm to meet its post-conditions, [networking.ts] introduces the DynamicBuffer concept:

> A dynamic buffer encapsulates memory storage that may be automatically resized as required, where the memory is divided into two regions: readable bytes followed by writable bytes. [buffer.reqmts.dynamicbuffer]

Signatures for algorithms in the technical specification which accept dynamic buffers use forwarding references:

>     // 17.10 [networking.ts::buffer.async.read.until], asynchronous delimited read operations:

>     template<
>       class AsyncReadStream,
>       class DynamicBuffer,
>       class CompletionToken>
>     DEDUCED async_read_until(
>       AsyncReadStream& s,
>       DynamicBuffer&& b,
>       char delim,
>       CompletionToken&& token);

Because the initiating function returns immediately, and the associated composed operation executes later, it is necessary for the algorithm to manage the lifetime of the dynamic buffer. Guidance for doing so is given in the TS:

> 13.2.7.5 Lifetime of initiating function arguments [async.reqmts.async.lifetime]

> 1. Unless otherwise specified, the lifetime of arguments to initiating functions shall be treated as follows: […] the implementation does not assume the validity of the argument after the initiating function completes […] The implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the completion handler.

Given the guidance above, the most sensible approach is for the implementation to move or make a decay-copy of the argument. An implementation of the TS, authored by the principal architect of the TS itself, does precisely that:

>     template <
>         typename AsyncReadStream,
>         typename DynamicBuffer,
>         typename ReadHandler>
>     class read_until_delim_op
>     {
>     public:
>         template <typename DeducedBuffers>
>         read_until_delim_op(
>             AsyncReadStream& stream,
>             DeducedBuffers&& buffers,
>             char delim, ReadHandler& handler)
>         : […]
>           buffers_(std::forward<DeducedBuffers>(buffers))
>           […]
>         {
>         }
>         […]
>         DynamicBuffer buffers_;
>         […]
>     };

>     template <
>         typename AsyncReadStream,
>         typename DynamicBuffer,
>         typename ReadHandler>
>     NET_TS_INITFN_RESULT_TYPE(ReadHandler,
>         void (std::error_code, std::size_t))
>     async_read_until(
>         AsyncReadStream& s,
>         DynamicBuffer&& buffers,
>         char delim,
>         ReadHandler&& handler)
>     {
>       // If you get an error on the following line it means that your handler does
>       // not meet the documented type requirements for a ReadHandler.
>       NET_TS_READ_HANDLER_CHECK(ReadHandler, handler) type_check;

>       async_completion<ReadHandler,
>         void (std::error_code, std::size_t)> init(handler);

>       detail::read_until_delim_op<
>         AsyncReadStream,
>         typename decay<DynamicBuffer>::type,
>         NET_TS_HANDLER_TYPE(ReadHandler, void (std::error_code, std::size_t))>(
>             s,
>             DynamicBuffer&&buffers,
>             delim,
>             init.completion_handler)(std::error_code(), 0, 1);

>       return init.result.get();
>     }

Given the semantics of dynamic buffers implied by the wording, instances of dynamic buffers behave more like references to storage types rather than storage types, as copies refer to the same underlying storage. This can be seen in the declaration of `dynamic_string_buffer` which meets the requirements of DynamicBuffer:

>     template <typename Elem, typename Traits, typename Allocator>
>     class dynamic_string_buffer
>     {
>       […]
>     private:
>       std::basic_string<Elem, Traits, Allocator>& string_;
>       std::size_t size_;
>       const std::size_t max_size_;
>     };

A dynamic string buffer contains a reference to the underlying string. Copies of a dynamic string buffer refer to the same string. Note that the dynamic string buffer also contains some state: the `size_` and `max_size_` data members. This additional metadata informs the dynamic string buffer of the boundaries between the readable and writable bytes, as well as the maximum allowed size of the total of the readable and writable bytes.

When only one composed operation handles the dynamic buffer, things seem to work. However, if a composed operation wishes to invoke another composed operation and pass that dynamic buffer, a problem arises. Consider two composed operations `f` and `g`, which both operate on an instance of dynamic buffer. When `f` is invoked, it makes a copy of the dynamic buffer and then calls `g` with the copy. At this point, `g` must also make a copy. Copies share the underlying storage, but in the case of dynamic string buffers each copy maintains its own distinct metadata. When `g` has finished mutating the dynamic buffer and returns control back to `f` by invoking the completion handler, the metadata in the copy of the dynamic buffer held by `f` will not have the changes made by `g`.

Another design problem caused by adding metadata to the dynamic buffer concept is illustrated in the following example code:

>     template<class MutableBufferSequence>
>     std::size_t read(const MutableBufferSequence&)
>     {
>       throw std::exception{};
>     }

>     int main()
>     {
>       std::string s;
>       assert(s.empty());
>       try
>       {
>         auto b = boost::asio::dynamic_buffer(s);
>         b.commit(read(b.prepare(32)));
>       }
>       catch(const std::exception&)
>       {
>         assert(s.empty()); // fails
>       }
>     }

While not technically incorrect, it may be surprising to the user that the string contains additional value-initialized data which was not part of the original readable bytes (which in this case was empty). The wording of the dynamic buffer concept does not address this case.

The solution we propose is to change the semantics of DynamicBuffer to represent a true storage type rather than a hybrid reference with metadata. Instances of dynamic buffers will be passed by reference, and callers will be required to manage the lifetime of dynamic buffer objects for the duration of any asynchronous operations. An additional benefit of this change is that it also solves the problem of exceptions described above.

[2018-06-18 after reflector discussion]

Status to LEWG; there will be a paper P1100R0 in the post-Rapperswil mailing addressing this.

[2020-05-28; Billy Baker comments]

From Cologne 2019 paper discussion: P1100 has been superseded by P1790.

[2025-10-16; Updated with LEWG position.]

Discussed by LEWG in Wrocław, recommended to send to SG4.

There's no 'SG4' status in the LWG issues lists, so it remains 'LEWG' but SG4 have been made aware.

Proposed resolution:

This wording is relative to N4734.

> [Drafting note: The project editor is kindly asked to replace all occurrences of `DynamicBuffer&&` with `DynamicBuffer&` as indicated by the provided wording changes below. — end drafting note]

1. Modify 16.1 [networking.ts::buffer.synop], header `<experimental/buffer>` synopsis, as indicated:

   >     […]
   >     // 16.11 [networking.ts::buffer.creation], buffer creation:
   >     […]

   >     template<class T, class Allocator>
   >     class dynamic_vector_buffer;

   >     template<class CharT, class Traits, class Allocator>
   >     class basic_dynamic_string_buffer;

   >     // 16.14 [networking.ts::buffer.dynamic.creation], dynamic buffer creation:

   >     template<class T, class Allocator>
   >       dynamic_vector_buffer<T, Allocator>
   >       dynamic_buffer(vector<T, Allocator>& vec) noexcept;
   >     template<class T, class Allocator>
   >       dynamic_vector_buffer<T, Allocator>
   >       dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept;

   >     template<class CharT, class Traits, class Allocator>
   >       dynamic_string_buffer<CharT, Traits, Allocator>
   >       dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
   >     template<class CharT, class Traits, class Allocator>
   >       dynamic_string_buffer<CharT, Traits, Allocator>
   >       dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;

   >     […]
   >     // 17.5 [networking.ts::buffer.read], synchronous read operations:
   >     […]

   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b);
   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec);
   >     template<class SyncReadStream, class DynamicBuffer, class CompletionCondition>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b,
   >                   CompletionCondition completion_condition);
   >     template<class SyncReadStream, class DynamicBuffer, class CompletionCondition>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b,
   >                   CompletionCondition completion_condition, error_code& ec);

   >     // 17.6 [networking.ts::buffer.async.read], asynchronous read operations:
   >     […]

   >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_read(AsyncReadStream& stream,
   >                          DynamicBuffer&& b, CompletionToken&& token);
   >     template<class AsyncReadStream, class DynamicBuffer,
   >       class CompletionCondition, class CompletionToken>
   >         DEDUCED async_read(AsyncReadStream& stream,
   >                            DynamicBuffer&& b,
   >                            CompletionCondition completion_condition,
   >                            CompletionToken&& token);

   >     // 17.7 [networking.ts::buffer.write], synchronous write operations:
   >     […]

   >     template<class SyncWriteStream, class DynamicBuffer>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&&; b);
   >     template<class SyncWriteStream, class DynamicBuffer>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b, error_code& ec);
   >     template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
   >                    CompletionCondition completion_condition);
   >     template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
   >                    CompletionCondition completion_condition, error_code& ec);

   >     // 17.8 [networking.ts::buffer.async.write], asynchronous write operations:
   >     […]

   >     template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_write(AsyncWriteStream& stream,
   >                           DynamicBuffer&& b, CompletionToken&& token);
   >     template<class AsyncWriteStream, class DynamicBuffer,
   >       class CompletionCondition, class CompletionToken>
   >         DEDUCED async_write(AsyncWriteStream& stream,
   >                             DynamicBuffer&& b,
   >                             CompletionCondition completion_condition,
   >                             CompletionToken&& token);

   >     // 17.9 [networking.ts::buffer.read.until], synchronous delimited read operations:

   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim);
   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
   >                         char delim, error_code& ec);
   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim);
   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
   >                         string_view delim, error_code& ec);

   >     // 17.10 [networking.ts::buffer.async.read.until], asynchronous delimited read operations:

   >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_read_until(AsyncReadStream& s,
   >                                DynamicBuffer&& b, char delim,
   >                                CompletionToken&& token);
   >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_read_until(AsyncReadStream& s,
   >                                DynamicBuffer&& b, string_view delim,
   >                                CompletionToken&& token);

   >     […]

2. Modify 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], as indicated:

   > -1- […]

   > -2- A type `X` meets the `DynamicBuffer` requirements if it satisfies the requirements of `Destructible` (C++ 2014 [destructible]) and `MoveConstructible` (C++ 2014 [moveconstructible]), as well as the additional requirements listed in Table 14.

3. Modify 16.12 [networking.ts::buffer.dynamic.vector], as indicated:

   > >     […]

   > >     template<class T, class Allocator = allocator<T>>
   > >     class dynamic_vector_buffer
   > >     {
   > >     public:
   > >       // types:
   > >       using value_type = vector<T, Allocator>;
   > >       using const_buffers_type = const_buffer;
   > >       using mutable_buffers_type = mutable_buffer;

   > >       // constructors:
   > >       dynamic_vector_buffer() = default;
   > >       explicit dynamic_vector_buffer(size_t maximum_size);
   > >       explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept;
   > >       dynamic_vector_buffer(vector<T, Allocator>& vec, size_t maximum_size) noexcept;
   > >       dynamic_vector_buffer(dynamic_vector_buffer&&) = default;

   > >       // members:
   > >       size_t size() const noexcept;
   > >       size_t max_size() const noexcept;
   > >       void max_size(size_t maximum_size);
   > >       size_t capacity() const noexcept;
   > >       const_buffers_type data() const noexcept;
   > >       mutable_buffers_type prepare(size_t n);
   > >       void commit(size_t n);
   > >       void consume(size_t n);
   > >       span<const T> get() const noexcept
   > >       value_type release();

   > >     private:
   > >       vector<T, Allocator>& vec_; // exposition only
   > >       size_t size_; // exposition only
   > >       const size_t max_size_; // exposition only
   > >     };

   > >     […]

   > -2- […]

   > -3- […]

   >     explicit dynamic_vector_buffer(size_t maximum_size)

   > > -?- Effects: Default-constructs `vec_`. Initializes `size_` with `0`, and `max_size_` with `maximum_size`.

   >     explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept

   > > -4- Effects: Initializes `vec_` with `move(vec)`, `size_` with `vec_.size()`, and `max_size_` with `vec_.max_size()`

   >     dynamic_vector_buffer(vector<T, Allocator>& vec,
   >                           size_t maximum_size) noexcept;

   > > -5- Requires: `vec.size() <= maximum_size`

   > > -6- Effects: Initializes `vec_` with `move(vec)`, `size_` with `vec_.size()`, and `max_size_` with `maximum_size`.

   > > […]

   >     size_t max_size() const noexcept;

   > > -8- Returns: `max_size_`.

   >     void max_size(size_t maximum_size)

   > > -?- Effects: Performs `max_size_ = maximum_size`.

   > […]

   >     void consume(size_t n);

   > > -15- Effects: […]

   >     span<const T> get() const noexcept

   > > -?- Returns: `span<const T>(vec_.data(), size_)`.

   >     value_type release()

   > > -?- Returns: `move(vec_)`.

4. Modify 16.13 [networking.ts::buffer.dynamic.string], as indicated:

   > >     template<class CharT, class Traits, class Allocator>
   > >     class basic_dynamic_string_buffer
   > >     {
   > >     public:
   > >       // types:
   > >       using value_type = basic_string<CharT, Traits, Allocator>;
   > >       using const_buffers_type = const_buffer;
   > >       using mutable_buffers_type = mutable_buffer;

   > >       // constructors:
   > >       basic_dynamic_string_buffer() = default;
   > >       explicit basic_dynamic_string_buffer(size_t maximum_size);
   > >       explicit basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept;
   > >       basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str, size_t maximum_size) noexcept;
   > >       basic_dynamic_string_buffer(basic_dynamic_string_buffer&&) = default;

   > >       // members:
   > >       size_t size() const noexcept;
   > >       size_t max_size() const noexcept;
   > >       void max_size(size_t maximum_size)
   > >       size_t capacity() const noexcept;
   > >       const_buffers_type data() const noexcept;
   > >       mutable_buffers_type prepare(size_t n);
   > >       void commit(size_t n) noexcept;
   > >       void consume(size_t n);
   > >       basic_string_view<CharT, Traits> get() const noexcept
   > >       value_type release();

   > >     private:
   > >       basic_string<CharT, Traits, Allocator>& str_; // exposition only
   > >       size_t size_; // exposition only
   > >       const size_t max_size_; // exposition only
   > >     };

   > >     using dynamic_string_buffer =
   > >       basic_dynamic_string_buffer<char, char_traits<char>, allocator<char>>

   > -2- […]

   > -3- […]

   >     explicit basic_dynamic_string_buffer(size_t maximum_size)

   > > -?- Effects: Default-constructs `str_`. Initializes `size_` with `0`, and `max_size_` with `maximum_size`.

   > > […]

   >     explicit basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept

   > > -4- Effects: Initializes `str_` with `move(str)`, `size_` with `str_.size()`, and `max_size_` with `str_.max_size()`

   >     basic_dynamic_string_buffer(string<CharT, Traits, Allocator>& str,
   >                                 size_t maximum_size) noexcept;

   > > -5- Requires: `str.size() <= maximum_size`.

   > > -6- Effects: Initializes `str_` with `move(str)`, `size_` with `str_.size()`, and `max_size_` with `maximum_size`.

   > > […]

   >     size_t max_size() const noexcept;

   > > -8- Returns: `max_size_`.

   >     void max_size(size_t maximum_size)

   > > -?- Effects: Performs `max_size_ = maximum_size`.

   > […]

   >     void consume(size_t n);

   > > -15- Effects: […]

   >     basic_string_view<CharT, Traits> get() const noexcept

   > > -?- Returns: `basic_string_view<CharT, Traits>(str_)`.

   >     value_type release()

   > > -?- Returns: `move(str_)`.

5. Remove 16.14 [networking.ts::buffer.dynamic.creation] entirely

6. Modify 17.5 [networking.ts::buffer.read], as indicated:

   >     […]

   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b);
   >     template<class SyncReadStream, class DynamicBuffer>
   >       size_t read(SyncReadStream& stream, DynamicBuffer&& b, error_code& ec);
   >     template<class SyncReadStream, class DynamicBuffer,
   >       class CompletionCondition>
   >         size_t read(SyncReadStream& stream, DynamicBuffer&& b,
   >                     CompletionCondition completion_condition);
   >     template<class SyncReadStream, class DynamicBuffer,
   >       class CompletionCondition>
   >         size_t read(SyncReadStream& stream, DynamicBuffer&& b,
   >                     CompletionCondition completion_condition,
   >                     error_code& ec);

   >     […]

7. Modify 17.6 [networking.ts::buffer.async.read], as indicated:

   >     […]

   >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_read(AsyncReadStream& stream,
   >                          DynamicBuffer&& b, CompletionToken&& token);
   >     template<class AsyncReadStream, class DynamicBuffer, class CompletionCondition,
   >       class CompletionToken>
   >         DEDUCED async_read(AsyncReadStream& stream,
   >                            DynamicBuffer&& b,
   >                            CompletionCondition completion_condition,
   >                            CompletionToken&& token);

   >     […]

   > -14- The program shall ensure both the `AsyncReadStream` object `stream` and the `DynamicBuffer` object `b` are is valid until the completion handler for the asynchronous operation is invoked.

8. Modify 17.7 [networking.ts::buffer.write], as indicated:

   >     […]

   >     template<class SyncWriteStream, class DynamicBuffer>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b);
   >     template<class SyncWriteStream, class DynamicBuffer>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b, error_code& ec);
   >     template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
   >                    CompletionCondition completion_condition);
   >     template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition>
   >       size_t write(SyncWriteStream& stream, DynamicBuffer&& b,
   >                    CompletionCondition completion_condition,
   >                    error_code& ec);

   >     […]

9. Modify 17.8 [networking.ts::buffer.async.write], as indicated:

   >     […]

   >     template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken>
   >       DEDUCED async_write(AsyncWriteStream& stream,
   >                           DynamicBuffer&& b, CompletionToken&& token);
   >     template<class AsyncWriteStream, class DynamicBuffer, class CompletionCondition,
   >       class CompletionToken>
   >         DEDUCED async_write(AsyncWriteStream& stream,
   >                             DynamicBuffer&& b,
   >                             CompletionCondition completion_condition,
   >                             CompletionToken&& token);

   >     […]

   > -14- The program shall ensure both the `AsyncWriteStream` object `stream` and the `DynamicBuffer` object `b` memory associated with the dynamic buffer `b` are valid until the completion handler for the asynchronous operation is invoked.

10. Modify 17.9 [networking.ts::buffer.read.until], as indicated:

    >     template<class SyncReadStream, class DynamicBuffer>
    >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b, char delim);
    >     template<class SyncReadStream, class DynamicBuffer>
    >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
    >                         char delim, error_code& ec);
    >     template<class SyncReadStream, class DynamicBuffer>
    >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b, string_view delim);
    >     template<class SyncReadStream, class DynamicBuffer>
    >       size_t read_until(SyncReadStream& s, DynamicBuffer&& b,
    >                         string_view delim, error_code& ec);

    >     […]

11. Modify 17.10 [networking.ts::buffer.async.read.until], as indicated:

    >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
    >       DEDUCED async_read_until(AsyncReadStream& s,
    >                                DynamicBuffer&& b, char delim,
    >                                CompletionToken&& token);
    >     template<class AsyncReadStream, class DynamicBuffer, class CompletionToken>
    >       DEDUCED async_read_until(AsyncReadStream& s,
    >                                DynamicBuffer&& b, string_view delim,
    >                                CompletionToken&& token);

    >     […]

    > -6- The program shall ensure both the `AsyncReadStream` object `stream` and the `DynamicBuffer` object `b` are is valid until the completion handler for the asynchronous operation is invoked.

### 3124(i). [networking.ts] Unclear how `execution_context` is intended to store services

Section: 13.7.5 [networking.ts::async.exec.ctx.globals] Status: New Submitter: Billy O'Neal III Opened: 2018-06-23 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

Addresses: networking.ts

`make_service` and `use_service` create arbitrary numbers of type `Service`, a type provided by the user, similar to how locale's `use_facet` works. As a result there's no amount of storage that could be reserved inside `execution_context` to avoid allocating memory. However, there's no allocator support here, and `make_service` is forbidden from throwing allocation related exceptions by N4734 [async.exec.ctx.globals]/7.

If the intent is for `execution_context` to allocate memory, are user overloads of `operator new` on type `Service` intended to be used?

[2018-07-20 Priority set to 3 after reflector discussion]

[Jonathan provides wording.]

Proposed resolution:

This wording is relative to the N4762.

1. Modify 13.7.5 [networking.ts::async.exec.ctx.globals] p7:

   -7- Throws: `service_already_exists` if a corresponding service object of type `Service::key_type` is already present in the set , `bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. Any exception thrown by the constructor of `Service`.

### 3126(i). There's no `std::sub_match::compare(string_view)` overload

Section: 28.6.8 [re.submatch] Status: Ready Submitter: Jonathan Wakely Opened: 2018-06-26 Last modified: 2026-06-10

Priority: 3

View all other issues in [re.submatch].

View all issues with Ready status.

Discussion:

`std::sub_match::compare` can be called with a `basic_string` or a pointer to a null-terminated character sequence, but can't be called with a `basic_string_view`. To compare to a `string_view` requires either conversion to `basic_string` (with a potential allocation) or a redundant call to `traits_type::length` to calculate a length that is already known.

[2018-07-02, Jonathan comments and completes proposed wording]

To make the relational and equality operators for `sub_match` support string views I propose specifying the semantics, not adding another 12 overloaded operators to namespace `std`, in addition to the 42 already there. This allows them to be implemented as "hidden friends" if the implementation so desires, or to retain namespace-scope declaration if backwards compatibility with C++11 - C++17 is preferred.

[2018-07-20 Priority set to 3 after reflector discussion]

Previous resolution [SUPERSEDED]

This wording is relative to N4750.

1. Change 28.6.3 [re.syn], header `<regex>` synopsis, as indicated:

   >     #include <initializer_list>

   >     namespace std {
   >       […]
   >       using csub_match = sub_match<const char*>;
   >       using wcsub_match = sub_match<const wchar_t*>;
   >       using ssub_match = sub_match<string::const_iterator>;
   >       using wssub_match = sub_match<wstring::const_iterator>;

   >       // 28.6.8.3 [re.submatch.op], sub_match non-member operators
   >       template<class BiIter>
   >       bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
   >       […]
   >       template<class BiIter>
   >         bool operator>=(const sub_match<BiIter>& lhs,
   >                        const typename iterator_traits<BiIter>::value_type& rhs);

   >       template<class charT, class ST, class BiIter>
   >       basic_ostream<charT, ST>&
   >       operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);
   >       […]
   >     }

2. Change 28.6.8 [re.submatch], class template `sub_match` synopsis, as indicated:

   >     namespace std {
   >       template<class BidirectionalIterator>
   >       class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> {
   >       public:
   >         using value_type =
   >           typename iterator_traits<BidirectionalIterator>::value_type;
   >         […]
   >         int compare(const sub_match& s) const;
   >         int compare(const string_type& s) const;
   >         int compare(const value_type* s) const;
   >         int compare(basic_string_view<value_type> s) const;
   >       };
   >     }

3. Change 28.6.8.2 [re.submatch.members] as indicated:

   >     int compare(const value_type* s) const;

   > > -7- Returns: `str().compare(s)`.

   >     int compare(basic_string_view<value_type> s) const;

   > > -?- Returns: `str().compare(s)`.

4. Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:

   > 31.9.2 `sub_match` non-member operators [re.submatch.op]

   >     template<class BiIter>
   >     bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   > > -1- Returns: `lhs.compare(rhs) == 0`.

   > […]

   >     template<class BiIter>
   >       bool operator>=(const sub_match<BiIter>& lhs,
   >         const typename iterator_traits<BiIter>::value_type& rhs);

   > > -42- Returns: `!(lhs < rhs)`.

   >     template<class charT, class ST, class BiIter>
   >       basic_ostream<charT, ST>&
   >         operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);

   > > -43- Returns: `os << m.str()`.

   > Class template `sub_match` provides overloaded relational operators (7.6.9 [expr.rel]) and equality operators (7.6.10 [expr.eq]) for comparisons with another `sub_match`, with a `string`, or with a single character. The expressions shown in Table ?? are valid when one of the operands is a type `S`, that is a specialization of `sub_match`, and the other expression is one of:

   > 1. (?.?) — a value `x` of a type `S`, in which case `STR(x)` is `x.str()`;

   > 2. (?.?) — a value `x` of type `basic_string<S::value_type, T, A>` for any types `T` and `A`, in which case `STR(x)` is `basic_string_view<S::value_type>(x.data(), x.length())`;

   > 3. (?.?) — a value `x` of type `basic_string_view<S::value_type, T>` for any type `T`, in which case `STR(x)` is `basic_string_view<S::value_type>(x.data(), x.length())`;

   > 4. (?.?) — a value `x` of a type convertible to `const S::value_type*`, in which case `STR(x)` is `basic_string_view<S::value_type>(x)`;

   > 5. (?.?) — a value `x` of type convertible to `S::value_type`, in which case `STR(x)` is `basic_string_view<S::value_type>(&x, 1)`.

   > Table ?? — `sub_match` comparisons
   > Expression | Return type | Operational semantics
   > --- | --- | ---
   > `s == t` | `bool` | `STR(s).compare(STR(t)) == 0`
   > `s != t` | `bool` | `STR(s).compare(STR(t)) != 0`
   > `s < t` | `bool` | `STR(s).compare(STR(t)) < 0`
   > `s > t` | `bool` | `STR(s).compare(STR(t)) > 0`
   > `s <= t` | `bool` | `STR(s).compare(STR(t)) <= 0`
   > `s >= t` | `bool` | `STR(s).compare(STR(t)) >= 0`

[2024-10-03; Jonathan rebases the wording on the latest WP]

The proposed resolution has been implemented and tested in libstdc++.

Previous resolution [SUPERSEDED]

This wording is relative to N4988.

1. Change 28.6.3 [re.syn], header `<regex>` synopsis, as indicated:

   >       using csub_match = sub_match<const char*>;
   >       using wcsub_match = sub_match<const wchar_t*>;
   >       using ssub_match = sub_match<string::const_iterator>;
   >       using wssub_match = sub_match<wstring::const_iterator>;

   >       // 28.6.8.3 [re.submatch.op], sub_match non-member operators
   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   >       template<class BiIter, class ST, class SA>
   >         bool operator==(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
   >       template<class BiIter, class ST, class SA>
   >         bool operator<=>(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   >       template<class BiIter, class ST>
   >         bool operator==(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);
   >       template<class BiIter, class ST>
   >         bool operator<=>(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);

   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs,
   >                         const typename iterator_traits<BiIter>::value_type* rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs,
   >                          const typename iterator_traits<BiIter>::value_type* rhs);

   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs,
   >                         const typename iterator_traits<BiIter>::value_type& rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs,
   >                          const typename iterator_traits<BiIter>::value_type& rhs);

   >       template<class charT, class ST, class BiIter>
   >         basic_ostream<charT, ST>&
   >           operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);

2. Change 28.6.8 [re.submatch], class template `sub_match` synopsis, as indicated:

   >     namespace std {
   >       template<class BidirectionalIterator>
   >       class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> {
   >       public:
   >         using value_type =
   >           typename iterator_traits<BidirectionalIterator>::value_type;
   >         […]
   >         int compare(const sub_match& s) const;
   >         int compare(const string_type& s) const;
   >         int compare(const value_type* s) const;
   >         int compare(basic_string_view<value_type> s) const;
   >       };
   >     }

3. Change 28.6.8.2 [re.submatch.members] as indicated:

   >     int compare(const value_type* s) const;

   > > -7- Returns: `str().compare(s)`.

   >     int compare(basic_string_view<value_type> s) const;

   > > -?- Returns: `str().compare(s)`.

4. Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:

   > 32.8.3 `sub_match` non-member operators [re.submatch.op]

   > -1- Let `SV(I)` be `basic_string_view<typename iterator_traits<I>::value_type>` and let `SM-CAT(I)` be `compare_three_way_result_t<basic_string<typename iterator_traits<I>::value_type>SV(I)>`

   >     template<class BiIter>
   >       bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   > > -2- Returns: `lhs.compare(rhs) == 0`.

   >     template<class BiIter>
   >       bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   > > -3- Returns: `static_cast<SM-CAT(BiIter)>(lhs.compare(rhs) <=> 0)`.

   >     template<class BiIter, class ST, class SA>
   >       bool operator==(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   > > -4- Returns: `lhs.compare(typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) == 0`.

   >     template<class BiIter, class ST, class SA>
   >       auto operator<=>(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   > > -5- Returns:

   > >     static_cast<SM-CAT(BiIter)>(lhs.compare(
   > >         typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size()))
   > >           <=> 0
   > >         )

   >     template<class BiIter, class ST>
   >       bool operator==(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);

   > > -?- Returns: `lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) == 0`.

   >     template<class BiIter, class ST>
   >       auto operator<=>(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);

   > > -?- Returns: `static_cast<SM-CAT(BiIter)>(lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) <=> 0)` .

[2026-06-10; Jonathan provides new wording]

Change `operator==` and `operator<=>` to take string view by value not by reference.

[Brno 2026-06-10; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N5046.

1. Change 28.6.3 [re.syn], header `<regex>` synopsis, as indicated:

   >       using csub_match = sub_match<const char*>;
   >       using wcsub_match = sub_match<const wchar_t*>;
   >       using ssub_match = sub_match<string::const_iterator>;
   >       using wssub_match = sub_match<wstring::const_iterator>;

   >       // 28.6.8.3 [re.submatch.op], sub_match non-member operators
   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   >       template<class BiIter, class ST, class SA>
   >         bool operator==(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
   >       template<class BiIter, class ST, class SA>
   >         bool operator<=>(
   >           const sub_match<BiIter>& lhs,
   >           const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   >       template<class BiIter, class ST>
   >         bool operator==(
   >           const sub_match<BiIter>& lhs,
   >           basic_string_view<typename iterator_traits<BiIter>::value_type, ST> rhs);
   >       template<class BiIter, class ST>
   >         bool operator<=>(
   >           const sub_match<BiIter>& lhs,
   >           basic_string_view<typename iterator_traits<BiIter>::value_type, ST> rhs);

   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs,
   >                         const typename iterator_traits<BiIter>::value_type* rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs,
   >                          const typename iterator_traits<BiIter>::value_type* rhs);

   >       template<class BiIter>
   >         bool operator==(const sub_match<BiIter>& lhs,
   >                         const typename iterator_traits<BiIter>::value_type& rhs);
   >       template<class BiIter>
   >         bool operator<=>(const sub_match<BiIter>& lhs,
   >                          const typename iterator_traits<BiIter>::value_type& rhs);

   >       template<class charT, class ST, class BiIter>
   >         basic_ostream<charT, ST>&
   >           operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);

2. Change 28.6.8 [re.submatch], class template `sub_match` synopsis, as indicated:

   >     namespace std {
   >       template<class BidirectionalIterator>
   >       class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> {
   >       public:
   >         using value_type =
   >           typename iterator_traits<BidirectionalIterator>::value_type;
   >         […]
   >         int compare(const sub_match& s) const;
   >         int compare(const string_type& s) const;
   >         int compare(const value_type* s) const;
   >         int compare(basic_string_view<value_type> s) const;
   >       };
   >     }

3. Change 28.6.8.2 [re.submatch.members] as indicated:

   >     int compare(const value_type* s) const;

   > > -7- Returns: `str().compare(s)`.

   >     int compare(basic_string_view<value_type> s) const;

   > > -?- Returns: `str().compare(s)`.

4. Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:

   > 32.8.3 `sub_match` non-member operators [re.submatch.op]

   > -1- Let `SV(I)` be `basic_string_view<typename iterator_traits<I>::value_type>` and let `SM-CAT(I)` be `compare_three_way_result_t<basic_string<typename iterator_traits<I>::value_type>SV(I)>`

   >     template<class BiIter>
   >       bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   > > -2- Returns: `lhs.compare(rhs) == 0`.

   >     template<class BiIter>
   >       bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);

   > > -3- Returns: `static_cast<SM-CAT(BiIter)>(lhs.compare(rhs) <=> 0)`.

   >     template<class BiIter, class ST, class SA>
   >       bool operator==(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   > > -4- Returns: `lhs.compare(typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) == 0`.

   >     template<class BiIter, class ST, class SA>
   >       auto operator<=>(
   >         const sub_match<BiIter>& lhs,
   >         const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);

   > > -5- Returns:

   > >     static_cast<SM-CAT(BiIter)>(lhs.compare(
   > >         typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size()))
   > >           <=> 0
   > >         )

   >     template<class BiIter, class ST>
   >       bool operator==(
   >         const sub_match<BiIter>& lhs,
   >         basic_string_view<typename iterator_traits<BiIter>::value_type, ST> rhs);

   > > -?- Returns: `lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) == 0`.

   >     template<class BiIter, class ST>
   >       auto operator<=>(
   >         const sub_match<BiIter>& lhs,
   >         basic_string_view<typename iterator_traits<BiIter>::value_type, ST> rhs);

   > > -?- Returns: `static_cast<SM-CAT(BiIter)>(lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) <=> 0)` .

### 3142(i). `std::foo<incomplete>` should be ill-formed NDR

Section: 16.4.5.8 [res.on.functions] Status: New Submitter: Casey Carter Opened: 2018-07-07 Last modified: 2018-11-27

Priority: 3

View all other issues in [res.on.functions].

View all issues with New status.

Discussion:

16.4.5.8 [res.on.functions]/2 states:

> -2- In particular, the effects are undefined in the following cases:

> […]

> (2.5) — if an incomplete type (6.9 [basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.

While undefined behavior is appropriate for the other cases specified in the earlier bullets, which describe failure to meet (runtime) semantic requirements, "instantiating a template component or evaluating a concept" with an incomplete type happens at compile-time, and could potentially be diagnosed. It would therefore be more appropriate to specify that programs which do so are ill-formed with no diagnostic required.

[2018-11 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.

Change 16.4.5.8 [res.on.functions] as indicated:

> (2.5) — if an incomplete type (6.9 [basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.

> -?- Unless explicitly stated otherwise, a program that instantiates a template component or evaluates a concept with an incomplete type (6.9 [basic.types]) as a template argument is ill-formed with no diagnostic required.

### 3157(i). Allocator `destroy` and fancy pointer operations must be non-throwing

Section: 16.4.4.6 [allocator.requirements] Status: New Submitter: Billy O'Neal III Opened: 2018-09-07 Last modified: 2018-12-16

Priority: 3

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

In annotating things required to be called by `~vector`, Casey pointed out that several operations I guarded with `noexcept` aren't actually mandated by the standard to be `noexcept`. However, the STL, and more specifically here, containers, consider inability to destroy an element an unrecoverable condition. This is evidenced for the whole STL by 16.4.6.14 [res.on.exception.handling]/3 "Every destructor in the C ++ standard library shall behave as if it had a non-throwing exception specification.".

As a result, `allocator::destroy` and fancy pointer operations must be non-throwing for valid input, or the containers don't make any sense. This is obvious for things like `vector::~vector`, but less obviously the containers rely on these guarantees whenever inserting more than one element, etc.

Moreover, we too narrowly specify the domain of the `pointer_traits::pointer_to` requirement in the `Cpp17Allocator` requirements, because any node-based container that uses container-internal sentinel nodes needs to be able to form pointers to said sentinel nodes; that operation must also be non-throwing.

[2018-09 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.

1. Modify 16.4.4.6 [allocator.requirements], Table 32 "Descriptive variable definitions" as indicated:

   > Table 32 — Descriptive variable definitions
   > Variable | Definition
   > --- | ---
   > `…`
   > `YY` | the type `allocator_traits<Y>`
   > `Z` | an allocator-aware container type (23.2.2 [container.requirements.general])
   > `…`
   > `y` | a value of type `XX::const_void_pointer` obtained by conversion from a result value of `YY::allocate`, or else a value of type (possibly `const`) `std::nullptr_t`.
   > `z` | an lvalue of type `Z` such that `z.get_allocator() == a`
   > `r1` | a reference to any member subobject of `z`
   > `n` | a value of type `XX::size_type`.
   > `…`

2. Modify 16.4.4.6 [allocator.requirements], Table 33 "`Cpp17Allocator` requirements" as indicated:

   > Table 33 — `Cpp17Allocator` requirements
   > Expression | Return type | Assertion/note pre-/post-condition | Default
   > --- | --- | --- | ---
   > `…`
   > `pointer_traits< X::pointer >::pointer_to(r)` | `X::pointer` | Ssame as `p`. Throws: Nothing. |
   > `pointer_traits< X::pointer >::pointer_to(r1)` |  | A value of type `YY::pointer` or `YY::const_pointer` `k` such that `*k` is `r1`. Throws: Nothing. |
   > `…`
   > `a.destroy(c)` | (not used) | Effects: Destroys the object at `c`. Throws: Nothing. | `c->~C()`
   > `…`

3. Modify 16.4.4.6 [allocator.requirements], p5, as indicated:

   > -5- An allocator type `X` shall satisfy the `Cpp17CopyConstructible` requirements (Table 26). The `X::pointer`, `X::const_pointer`, `X::void_pointer`, and `X::const_void_pointer` types shall satisfy the `Cpp17NullablePointer` requirements (Table 30). No constructor, comparison function, copy operation, move operation, or swap operation on these pointer types shall exit via an exception. `X::pointer` and `X::const_pointer` shall also satisfy the requirements for a random access iterator (24.3.5.7 [random.access.iterators]) and of a contiguous iterator (24.3.1 [iterator.requirements.general]) and operations in those requirements shall not exit via an exception so long as resulting iterators are dereferencable or past-the-end.

4. Modify 20.2.9.3 [allocator.traits.members], as indicated:

   >     template<class T>
   >       static void destroy(Alloc& a, T* p);

   > > -6- Effects: Calls `a.destroy(p)` if that call is well-formed; otherwise, invokes `p->~T()`.

   > > -?- Throws: Nothing.

### 3159(i). §[unique.ptr.single] requirements on deleter may be too strict

Section: 20.3.1.3 [unique.ptr.single] Status: New Submitter: Jonathan Wakely Opened: 2018-09-17 Last modified: 2018-10-06

Priority: 3

View other active issues in [unique.ptr.single].

View all other issues in [unique.ptr.single].

View all issues with New status.

Discussion:

20.3.1.3 [unique.ptr.single] p1 says:

> The default type for the template parameter `D` is `default_delete`. A client-supplied template argument `D` shall be a function object type (19.14), lvalue reference to function, or lvalue reference to function object type for which, given a value `d` of type `D` and a value ptr of type `unique_ptr<T, D>::pointer`, the expression `d(ptr)` is valid and has the effect of disposing of the pointer as appropriate for that deleter.

That means this is undefined:

>     #include <memory>

>     struct IncompleteBase;

>     struct Deleter {
>       void operator()(IncompleteBase*) const;
>     };

>     struct IncompleteDerived;

>     struct X {
>       std::unique_ptr<IncompleteDerived, Deleter> p;
>       ~X();
>     };

`unique_ptr::pointer` is `IncompleteDerived*`, but `is_invocable<Deleter, IncompleteDerived*>` is unknowable until the type is complete (see LWG 3099(i) etc).

The intention is that `IncompleteDerived` only needs to be complete when the deleter is invoked, which is in the definition of `X::~X()` for this example. But the requirement for `d(ptr)` to be valid requires a complete type. If the `unique_ptr` implementation adds `static_assert(is_invocable_v<D, pointer>)` to enforce the requirement, the example above fails to compile. GCC recently added that assertion.

Do we want to relax that requirement, or do we want to force the code above to define `Deleter::pointer` as `IncompleteBase*` so that the `is_invocable` condition can be checked?

The destructor and `reset` member function already require that the deleter can be invoked (and that Requires: element will be turned into a Mandates: one soon). We can just remove that requirement from the preamble for the class template, or say that the expression only needs to be valid when the destructor and `reset` member are instantiated. We could also rephrase it in terms of `is_invocable_v<D, unique_ptr<T, D>::pointer>`.

[2018-10 Reflector prioritization]

Set Priority to 3

Proposed resolution:

### 3166(i). No such descriptive element as Value:

Section: 22.3.4 [pair.astuple], 22.6.4 [variant.helper], 23.3.3.7 [array.tuple], 20.2.3.2 [pointer.traits.types], 20.2.9.2 [allocator.traits.types], 20.6.2 [allocator.adaptor.types] Status: New Submitter: Walter Brown Opened: 2018-11-08 Last modified: 2020-09-06

Priority: 3

View all other issues in [pair.astuple].

View all issues with New status.

Discussion:

In N4778, 22.3.4 [pair.astuple], 22.6.4 [variant.helper], and 23.3.3.7 [array.tuple], are partly specified via a fictitious descriptive element Value: Moreover, 99 [span.tuple] is on track to do likewise in the near future.

Let's invent such a Value: element and properly document it within 16.3.2.4 [structure.specifications], or else let's respecify the offending uses.

[2018-11 Reflector prioritization]

Set Priority to 3

[2020-05-01; Daniel comments]

It should be pointed out that the originally referred to Value: element has since then be renamed to Type: but the reported problem (its lack of definition) still exists in N4861.

Proposed resolution:

### 3167(i). [fund.ts.v3] Does `observer_ptr` support function types?

Section: 8.2 [fund.ts.v3::memory.observer.ptr] Status: Open Submitter: Alisdair Meredith Opened: 2018-11-14 Last modified: 2022-10-12

Priority: 3

View all issues with Open status.

Discussion:

Addresses: fund.ts.v3

From the wording, function pointers are never clearly considered. P1 infers support for only objects, but it is not clear that wording is intended to be a deliberate restriction, or is casual wording.

Paragraph 2 mandates that we cannot instantiate for reference types, and do support incomplete types, but still does not consider function types. Calling out references specifically, as they are not object types, suggests the inferred support for only objects in p1 is more a case of casual phrasing, than deliberate intent.

However, if we did intend to support function pointers, we may want to consider adding a function-call operator, constrained to supporting pointer-to function types. One other possibility is that the explicit conversion to `pointer` already serves that purpose, although I need to double-check the core language that contextual conversions to function pointer are considered when invoking the function call operator. If this is the case, then I suggest a note to that effect in [memory.observer.ptr.conv] instead.

[2018-11 Reflector prioritization]

Set Priority to 3

[2022-10-12 LWG telecon]

Discussed on reflector in July 2022, no consensus on how `observer_ptr` is even meant to be used.

`shared_ptr` supports function types, no good reason to disallow them here.

No desire to make any change for LFTSv3, but keep the issue open until/unless the TS is withdrawn. That way we will be less likely to forget about this if `observer_ptr` is propsed for the IS.

Proposed resolution:

### 3172(i). 3-arg `std::hypot` is underspecified compared to the 2-arg overload

Section: 29.7.3 [c.math.hypot3] Status: Ready Submitter: Matthias Kretz Opened: 2018-12-06 Last modified: 2026-06-15

Priority: 3

View all issues with Ready status.

Discussion:

The 2-arg `hypot` function specified in the C standard is required to avoid overflow and underflow (7.12.7.3 p2). Furthermore C's Annex F (IEC 60559 floating-point arithmetic) defines special cases for inputs of `±0` and `±inf` (F. 10.4.3). The 3-arg `hypot` function 29.7.3 [c.math.hypot3] is only specified as "Returns: x 2+ y 2+z 2.". This is inconsistent with the 2-arg overload.

It is not clear whether C's Annex F is "imported" into the C++ standard. [cmath.syn] p1 suggests that it is: "The contents and meaning of the header `<cmath>` are the same as the C standard library header `<math.h>`, […]".

[2018-12-21 Reflector prioritization]

Set Priority to 3

Previous resolution [SUPERSEDED]

This wording is relative to N4778.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A

1. Modify 29.7.3 [c.math.hypot3] as indicated:

   > >     float hypot(float x, float y, float z);
   > >     double hypot(double x, double y, double z);
   > >     long double hypot(long double x, long double y, long double z);

   > > > -?- Effects: The `hypot` functions compute the square root of the sum of the squares of `x`, `y`, and `z`, without undue overflow or underflow. A range error may occur.

   > > > -1- Returns: x 2+ y 2+z 2.

   > > > -?- Remarks: If `__STDC_IEC_559__` is defined, the following guarantees hold:

   > > > - `hypot(x, y, z)`, `hypot(x, z, y)`, `hypot(z, y, x)`, and `hypot(x, y, -z)` are equivalent.

   > > > - if `y2 + z2 == ±0`, `hypot(x, y, z)` is equivalent to `fabs(x)`.

   > > > - `hypot(±∞, y, z)` returns `+∞`, even if `y` and/or `z` is a NaN.

Option B

1. Add a note that clarifies that the behavior of the 3-arg `hypot` function differs from the C specification. I.e. that no special guarantees wrt. over-/underflow or special values are given.

[2026-06-10; Tim provides new wording]

LWG in Brno is in favor of the direction of Option A. However, `__STDC_IEC_559__` is not defined in C++. The wording has been updated following the current C WD, taking into account WG14 N2714.

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5060.

1. Modify 29.7.3 [c.math.hypot3] as indicated:

   > >     constexpr floating-point-type hypot(floating-point-type x, floating-point-type y,
   > >                                         floating-point-type z);

   > > > -?- Effects: Compute the square root of the sum of the squares of `x`, `y`, and `z`, without undue overflow or underflow. A range error may occur.

   > > > -1- Returns: x 2+ y 2+z 2.

   > > > -?- Remarks: If `numeric_limits<floating-point-type>::is_iec559` is `true`, the following guarantees hold:

   > > > - `hypot(x, y, z)`, `hypot(x, z, y)`, `hypot(z, y, x)`, and `hypot(x, y, -z)` are equivalent.

   > > > - if `y` and `z` are zero and `x` is not a NaN, `hypot(x, y, z)` returns the absolute value of `x`.

   > > > - if `x` is positive or negative infinity, `hypot(x, y, z)` returns positive infinity, even if `y` and/or `z` is a NaN.

   > > > - if `x` is a NaN and neither `y` nor `z` is positive or negative infinity, `hypot(x, y, z)` returns a NaN.

### 3174(i). Precondition on `is_convertible` is too strong

Section: 21.3.7 [meta.rel] Status: New Submitter: Casey Carter Opened: 2018-12-09 Last modified: 2019-03-16

Priority: 3

View other active issues in [meta.rel].

View all other issues in [meta.rel].

View all issues with New status.

Discussion:

Per Table 49 in 21.3.7 [meta.rel], the preconditions for both `is_convertible<From, To>` and `is_nothrow_convertible<From, To>` are:

> `From` and `To` shall be complete types, arrays of unknown bound, or cv `void` types.

Consequently, this program fragment:

>     struct S;
>     static_assert(is_convertible_v<S, const S&>);

has undefined behavior despite that the actual behavior of `is_convertible` specified in [meta.rel]/5:

> -5- The predicate condition for a template specialization `is_convertible<From, To>` shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

> >     To test() {
> >       return declval<From>();
> >     }

> [ Note: …]

is well-formed: `declval<S>()` is an xvalue of type `S`, which certainly does implicitly convert to `const S&`. We should relax the precondition to allow this perfectly valid case (and similar cases like `is_convertible<S, S&&>`), letting the cases that would in fact be invalid fall through to the blanket "incompletely-defined object type" wording in [meta.rqmts]/5.

[2018-12-21 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4791.

1. Modify Table 49 in 21.3.7 [meta.rel] as follows:

   Table 49 — Type relationship predicates
   Template | Condition | Comments
   --- | --- | ---
   […] | […] | […]
       template<class From, class To>
       struct is_convertible;
   | see below | `From` and `To` shall be a complete types, an arrays of unknown bound, or cv `void` types.
       template<class From, class To>
       struct is_nothrow_convertible;
   | `is_convertible_v<From, To>` is `true` and the conversion, as defined by `is_convertible`, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]) | `From` and `To` shall be a complete types, an arrays of unknown bound, or cv `void` types.
   […] | […] | […]

### 3188(i). `istreambuf_iterator::pointer` should not be unspecified

Section: 24.6.4 [istreambuf.iterator] Status: New Submitter: Jonathan Wakely Opened: 2019-02-21 Last modified: 2019-10-30

Priority: 3

View other active issues in [istreambuf.iterator].

View all other issues in [istreambuf.iterator].

View all issues with New status.

Discussion:

The current working paper requires `iterator_traits<Iter>::pointer` to be `void` if `Iter` doesn't define `operator->` (24.3.2.3 [iterator.traits]). We recently removed `operator->` from `istreambuf_iterator` via LWG 2790(i), therefore either its `pointer` member should be `void`, or there should be a partial specialization of `iterator_traits`. Do we want to change unspecified to `void` in the class synopsis in [istreambuf.iterator]?

[2019-03-03, Daniel provides concrete wording]

[2019-03-05 Priority set to 3 after reflector discussion]

[2019-03-05, Daniel comments]

With the acceptance of P1252R2 the committee decided to deprecate `operator->` of `move_iterator`, interestingly without mentioning what should happen with its current `pointer` typedef (which is equal to the template parameter `Iterator` and thus never `void`). Effectively this is a very similar situation as for the here discussed `istreambuf_iterator` case and it seems attractive to me to solve both cases similarly.

[2019-10-30, Jonathan comments]

Also, `reverse_iterator::operator->()` is now constrained and so not always defined, but `reverse_iterator::pointer` is defined unconditionally.

Proposed resolution:

This wording is relative to N4800.

1. Change class template `istreambuf_iterator` synopsis, 24.6.4 [istreambuf.iterator], as indicated:

   >     template<class charT, class traits = char_traits<charT>>
   >     class istreambuf_iterator {
   >     public:
   >       using iterator_category = input_iterator_tag;
   >       using value_type        = charT;
   >       using difference_type   = typename traits::off_type;
   >       using pointer           = voidunspecified;
   >       using reference         = charT;
   >       […]
   >     };

### 3189(i). Missing requirement for `std::priority_queue`

Section: 23.6.4 [priority.queue] Status: New Submitter: Jonathan Wakely Opened: 2019-02-21 Last modified: 2019-03-05

Priority: 3

View all other issues in [priority.queue].

View all issues with New status.

Discussion:

We don't require that the `Compare` template parameter can be invoked with arguments of the queue's value type. It seems like something we can use Mandates: for, since it'll be ill-formed otherwise. Something like `is_invocable_r_v<bool, Compare&, value_type&, value_type&>`.

This might also apply to the `Compare` parameter for the `merge` and `sort` functions of `forward_list` and `list`.

[2019-03-05 Priority set to 3 after reflector discussion]

Proposed resolution:

### 3193(i). Mandates: and Expects: elements are not defined for types

Section: 16.3.2.4 [structure.specifications] Status: New Submitter: Daniel Krügler Opened: 2019-03-04 Last modified: 2020-06-11

Priority: 3

View other active issues in [structure.specifications].

View all other issues in [structure.specifications].

View all issues with New status.

Discussion:

The working paper uses the special elements Mandates:, Expects: as well as Requires: to types, albeit 16.3.2.4 [structure.specifications] defines them only for functions, for example 16.3.2.4 [structure.specifications] sub-bullet (3.4):

> Expects: the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is called.

Examples for such usages on types are (from N4800):

- 27.2.3 [char.traits.typedefs] for types `int_type` and `state_type`

- 22.3.4 [pair.astuple] for `tuple_element<I, pair<T1, T2>>::type`

- 22.4.7 [tuple.helper] for `tuple_element<I, tuple<Types...>>::type`

- 22.4.11 [tuple.traits] for `uses_allocator<tuple<Types...>, Alloc>`

- Table 62 — "Container requirements" for type `XX::value_type`

- Table 65 — "Allocator-aware container requirements" for type `allocator_type`

- Table 69 — "Associative container requirements" for types `X::value_type` and `X::key_compare`

- Table 70 — "Unordered associative container requirements" for types `X::value_type` and `X::key_equal`

Instead of replacing these elements usages for these places by extra wording to reach the same effects I recommend to update instead 16.3.2.4 [structure.specifications] to ensure that requirement-expressing elements are defined in a way that it also allows to express requirements imposed on types by these elements to standardize "existing practice".

Considering details, it seems obvious that Mandates:, Expects: as well as Requires: are "suitable" to be defined for types (With the acceptance of P1463R1 there are now also Mandates: for types such as Table 65 — "Allocator-aware container requirements" for type `allocator_type`).

For Constraints: the meaning would not be so clear: Should it mean that there is conditionally a type defined or not? According to the submitters knowledge there are currently no known examples for Constraints: to specify constraint on types, therefore I'm suggesting to restrict this extension to Mandates:, Expects:, and Requires: alone.

[2019-03-15 Priority set to 3 after reflector discussion]

[2019-03-15; Daniel comments and provides wording]

During the preparation of the wording for this issue it was found that we should allow Remarks: elements to be used for other things than functions. One example of imposed restrictions can be found in 17.12.3 [cmp.common]:

>     template<class... Ts>
>     struct common_comparison_category {
>       using type = see below;
>     };

> -2- Remarks: The member typedef-name `type` denotes the common comparison type (11.10.3 [class.spaceship]) of `Ts...`, the expanded parameter pack. […]

The discussion of this issue speaks of "type" restrictions (versus the specified restrictions on functions), because even the non-type template argument restrictions of 22.3.4 [pair.astuple] appear in the context of a member type specification, but there are examples where not really a single (member) type is involved, e.g. in the 22.4.7 [tuple.helper] example mentioned above.

Another example is when such elements are used for the specification of template specializations, e.g. in 22.4.7 [tuple.helper]:

>     template<class T> struct tuple_size;

> > -1- Remarks: All specializations of `tuple_size` shall satisfy the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic of `integral_constant<size_t, N>` for some `N`.

Besides class template specializations, a second relevant use-case is the specification of member types (Which are not necessarily part of a template), typically within the requirement tables, e.g. in Table 62 — "Container requirements"'s entry `X::value_type`:

> Requires: `T` is Cpp17Erasable from `X`

The suggested wording tries to cover the generalization by means of the term "non-function entities" in addition to the existing functions to prevent being enforced to enumerate all entities to which the extended rules apply.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4810.

> 1. Change 16.3.2.4 [structure.specifications], as indicated:

>    > -3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])

>    > 1. (3.1) — Requires: the preconditions imposed on a non-function entity, or for calling the function.

>    > 2. (3.2) — Constraints: […]

>    > 3. (3.3) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (Clause 9). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]

>    > 4. (3.4) — Expects: the conditions (sometimes termed preconditions) imposed on a non-function entity, or that the function assumes to hold whenever it is called. [Example: An implementation might express such conditions via an attribute such as `[[expects]]` ( [dcl.attr.contract]) on a function declaration. However, some such conditions might not lend themselves to expression via code. — end example]

>    > 5. […]

>    > 6. (3.11) — Remarks: additional semantic constraints on the function.

>    > 7. […]

> 2. Change 99 [res.on.required], as indicated:

>    > -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when thea function's precondition is violated.

>    > -2- Violation of any preconditions specified in an function's Expects: element results in undefined behavior.

[2020-05-01; Daniel comments and adjusts wording to recent working draft]

It should be pointed out that the originally referred to Expects: element has since then be renamed to Preconditions: and that the Requires: element does now only occur in annex D.

[2020-06-11; Jonathan comments]

This issue also affects some type traits such as `alignment_of` and `make_signed`/`make_unsigned`. In addition to clarifying what Mandates: means on a non-function we need to decide exactly what is being mandated in the type traits. Is instantiating the class template ill-formed, or just odr-using the nested `type` or `value` member?

Proposed resolution:

This wording is relative to N4861.

1. Change 16.3.2.4 [structure.specifications], as indicated:

   > -3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])

   > 1. (3.1) — Constraints: […]

   > 2. (3.2) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (9.1 [dcl.pre]). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]

   > 3. (3.3) — Preconditions: the conditions imposed on a non-function entity, or that the function assumes to hold whenever it is called.

   > 4. […]

   > 5. (3.10) — Remarks: additional semantic constraints on the function.

   > 6. […]

2. Change [res.on.expects], as indicated:

   > -1- Violation of any preconditions specified in a function's Preconditions: element results in undefined behavior.

3. Change [depr.res.on.required], as indicated:

   > [Drafting note: Interestingly, albey the Requires: element has nearly vanished, the issue is still relevant, see D.13 [depr.meta.types]]

   > -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when thea function's precondition is violated.

### 3197(i). `std::prev` should not require `BidirectionalIterator`

Section: 24.4.3 [iterator.operations] Status: New Submitter: Billy O'Neal III Opened: 2019-04-03 Last modified: 2024-06-18

Priority: 3

View other active issues in [iterator.operations].

View all other issues in [iterator.operations].

View all issues with New status.

Discussion:

MSVC++ (and apparently libc++) have asserts that `std::prev` only accepts `BidirectionalIterator`s, because it's declared in the standard as accepting only `BidirectionalIterator`. libc++ changed their tests (in this commit), apparently from a bug report from Ville and Jonathan, saying that one could theoretically call `std::prev` with a negative number.

The standardese in [iterator.operations] strongly indicates that `prev` requires a `BidirectionalIterator`, but I don't see the usual wording that connects template type parameters of that name to the `<algorithm>` requirements or similar. So perhaps one could argue that the name `Bidirectional` there has no meaning. Even if that is the case, that's a defect in the other direction.

[2019-06-12 Priority set to 3 after reflector discussion]

[2022-04-22; Jonathan adds a comment]

P2408 changes the requirements for types substituting `BidirectionalIterator` etc. in the Algorithms clause. We should consider whether that is appropriate here, especially as algorithms might make use of `std::prev` internally. An algorithm that was changed by P2408 to accept types that model `bidirectional_iterator` instead of requiring `Cpp17BidirectionalIterator` might have to stop using `std::prev` if we don't resolve this issue to allow it.

We should consider whether `distance`, `advance` and `next` need the same treatment.

[2024-06-18; Jonathan adds a comment]

Related to LWG 2353(i) which made a similar change to `std::next`. Also, if we require a Cpp17BidirectionalIterator here, then that means you can't use `std::prev` on a `std::bidirectional_iterator` unless it also meets the Cpp17BidirectionalIterator requirements. That seems like an unnecessary restriction, since `std::prev` doesn't do anything that wouldn't work fine with any type that models `std::bidirectional_iterator`.

Proposed resolution:

This wording is relative to N4810.

> [Drafting Note: Three mutually exclusive options are prepared, depicted below by Option A, Option B, and Option C, respectively.]

Option A

1. NAD, the name `BidirectionalIterator` actually means that `prev` requires bidirectional iterators, in which case this change to libcxx is incorrect.

Option B

1. Modify 24.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     // 24.4.3 [iterator.operations], iterator operations
   >     […]
   >     template<class BidirectionalInputIterator>
   >       constexpr BidirectionalInputIterator prev(BidirectionalInputIterator x,
   >         typename iterator_traits<BidirectionalInputIterator>::difference_type n = 1);

2. Modify 24.4.3 [iterator.operations] as indicated:

   >     template<class BidirectionalInputIterator>
   >       constexpr BidirectionalInputIterator prev(BidirectionalInputIterator x,
   >         typename iterator_traits<BidirectionalInputIterator>::difference_type n = 1);

   > > -7- Effects: Equivalent to: `advance(x, -n); return x;`

Option C

1. The intent of the wording is that the template parameters apply requirements, and the defect is that they do not. We should add a requirement in 24.4.3 [iterator.operations]/1 to the effect that the template parameter names impose said requirements.

### 3205(i). `decay_t` in the new `common_type` fallback should be `remove_cvref_t`

Section: 21.3.8.7 [meta.trans.other] Status: New Submitter: Casey Carter Opened: 2019-05-12 Last modified: 2022-04-25

Priority: 3

View all other issues in [meta.trans.other].

View all issues with New status.

Discussion:

P0898R4 "The One Ranges Proposal" added a new fallback case to the definition of `common_type` in 21.3.8.7 [meta.trans.other], bullet 3.3.4:

> Otherwise, if `COND_RES(CREF(D1), CREF(D2))` denotes a type, let `C` denote the type `decay_t<COND_RES(CREF(D1), CREF(D2))>`.

Per para 3.3, `D1` and `D2` are decayed types. If both are `void`, bullet 3.3.4 is not reached. If either is an abominable function type or `void`, the `COND_RES` type expression above is ill-formed and bullet 3.3.4 does not apply. In all cases in which the `COND_RES` expression is well-formed, `D1` and `D2` denote cv-unqualified non-array object types. Given that fact, (1) `CREF(D1)` and `CREF(D2)` are equivalent to `const D1&` and `const D2&`, respectively, and (2) the `COND_RES` expression is equivalent to `decltype(false ? declval<const D1&>() : declval<const D1&>())`, i.e., the second and third operands of the conditional operator are lvalues of type `const D1` and `const D2`, respectively.

[expr.cond]/3 cannot apply since the operands are not glvalue bit-fields.

If `D1` and `D2` are the same type, [expr.cond]/4 does not apply. If `D1` and `D2` are different types, there are a few cases to consider:

1. If [expr.cond]/4.1 applies, one operand is converted into an lvalue reference to the type of the other, i.e., both resulting operands are lvalues of type either `const D1` or `const D2`.

2. [expr.cond]/4.2 cannot apply since neither operand is an xvalue.

3. [expr.cond]/4.3.1 cannot apply since it would imply that the operands have the same type.

4. If [expr.cond]/4.3.2 applies — if either `D1` or `D2` is a base class of the other — again the resulting operands are lvalues of type either `const D1` or `const D2`.

5. If [expr.cond]/4.3.3 applies, the either the `const D1&` operand converts to `const D2` or the `const D2&` operand converts to `const D1`.

6. If none of the sub-bullets in [expr.cond]/4 applies, the operands are left unchanged.

[expr.cond]/5 applies if the operands initially had the same type, or in cases 1 and 4 above. The conditional expression is an lvalue of type `const D1` or `const D2`, and the `COND_RES` expression yields `const D1&` or `const D2&`.

Only cases 5 and 6 reach [expr.cond]/6. This paragraph performs overload resolution, which may result in converting both operands to the same non-class type to invoke a builtin conditional operator "overload".

[expr.cond]/7 applies standard conversions including array-to-pointer and function-to-pointer conversion to the operands. Consequently, the operands are once more "decayed" if [expr.cond]/6 converted them to an array or function type. Again case-by-case:

1. [expr.cond]/7.1 applies if the operands now have the same type, which is the type of the conditional expression.

2. [expr.cond]/7.2 applies if the operands have arithmetic or enumeration type; the conditional expression yields the result of applying the usual arithmetic conversions.

3. [expr.cond]/7.3 applies if the operands have pointer type; the conditional expression yields their composite pointer type.

4. [expr.cond]/7.4 applies if the operands have pointer-to-member type; the conditional expression applies some more standard conversions and yields their composite pointer type.

5. [expr.cond]/7.5 applies if one operand has type `nullptr_t` and the other is either a null pointer constant or has type `nullptr_t`; the conditional expression yields `nullptr_t`.

In every case above, the conditional expression is either ill-formed, an lvalue of type `const D1` or `const D2`, or a prvalue of a non-array non-function type. Consequently the `COND_RES` type expression always yields a non-array non-function type, for which `decay_t` and `remove_cvref_t` are equivalent. We can therefore replace `COND_RES(CREF(D1), CREF(D2))` in [meta.trans.other]/3.3.4 with `decltype(false ? declval<const D1&>() : declval<const D2&>())`, and replace the usage of `decay_t` with `remove_cvref_t`.

Furthermore, there are now quite a few different cases describing the behavior of `common_type`. It's not clear that `common_type<T...>::type` is always a decayed type without in-depth analysis. We should non-normatively clarify that fact.

[2019-06-12 Priority set to 3 after reflector discussion]

[2020-05-01; Daniel adjusts wording to recent working draft]

[2022-04-25; Daniel adjusts wording to recent working draft]

Proposed resolution:

This wording is relative to N4910.

1. Modify 21.3.8.7 [meta.trans.other] as indicated:

   > -2- Let:

   > 1. (2.1) — `CREF(A)` be `add_lvalue_reference_t<const remove_reference_t<A>>`,

   > 2. (2.2) — […]

   > 3. […]

   > 4. (2.9) — […]

   > If any of the types computed above is ill-formed, then `COMMON-REF(A, B)` is ill-formed.

   > -3- Note A: For the `common_type` trait applied to a template parameter pack `T` of types, the member `type` shall be either defined or not present as follows:

   > 1. (3.1) — […]

   > 2. (3.2) — […]

   > 3. (3.3) — If `sizeof...(T)` is two, let the first and second types constituting `T` be denoted by `T1` and `T2`, respectively, and let `D1` and `D2` denote the same types as `decay_t<T1>` and `decay_t<T2>`, respectively.

   >    1. (3.3.1) — […]

   >    2. (3.3.2) — […]

   >    3. (3.3.3) — Otherwise, if

   >       >     decay_t<decltype(false ? declval<D1>() : declval<D2>())>

   >       denotes a valid type, let `C` denote that type.

   >    4. (3.3.4) — Otherwise, if `COND-RES(CREF(D1), CREF(D2))`

   >       >     remove_cvref_t<decltype(false ? declval<const D1&>() : declval<const D2&>())>

   >       denotes a type, let `C` denote theat type `decay_t<COND-RES(CREF(D1), CREF(D2))>`.

   > 4. (3.4) — […]

   > [Note: Whenever the qualified-id `common_type<T...>::type` is valid, it denotes the same type as `decay_t<common_type<T...>::type>`. — end note]

   > -4- Note B: […]

### 3214(i). §[facet.num.get.virtuals] doesn't say what it means for digit grouping to be consistent

Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: New Submitter: Jonathan Wakely Opened: 2019-06-03 Last modified: 2019-08-23

Priority: 4

View other active issues in [facet.num.get.virtuals].

View all other issues in [facet.num.get.virtuals].

View all issues with New status.

Discussion:

28.3.4.3.2.3 [facet.num.get.virtuals] paragraph 4 says:

> "Digit grouping is checked. That is, the positions of discarded separators is examined for consistency with `use_facet<numpunct<charT>>(loc).grouping()`. If they are not consistent then `ios_base::failbit` is assigned to `err`."

It's unclear what is considered consistent or not.

Obviously if the expected grouping is "1,234,567" then an input of "1,234,567" is consistent. Libstdc++, MSVC and Boost all consider "1234567" to be consistent with an expected grouping "1,234,567" (and it looks like libc++ is going to agree soon). That can be justified by saying that there are no discarded separators to examine, so no inconsistency. But what about "1234,567"? There is only one discarded separator here, and its position is consistent with the expected format.

The wording should clarify that if there are no separators at all, that is OK. If there are one or more separators then they must be at the expected positions, and there must not be any missing.

[2019-07 Issue Prioritization]

Priority to 4 after discussion on the reflector.

Proposed resolution:

### 3215(i). `variant` default constructor has vague `constexpr` requirements

Section: 22.6.3.2 [variant.ctor] Status: New Submitter: Louis Dionne Opened: 2019-06-04 Last modified: 2026-06-11

Priority: 2

View other active issues in [variant.ctor].

View all other issues in [variant.ctor].

View all issues with New status.

Discussion:

In 22.6.3.2 [variant.ctor] p5, we say:

> Remarks: This function shall be `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a constexpr function. […]

First of all, I find it confusing that we say "This function shall be `constexpr` if […]", when the declaration of the function clearly has the `constexpr` keyword on it unconditionally. Instead, I would use the wording "This function shall be usable in a constexpr context if […]".

Secondly, I think we shouldn't be using if-and-only-if since it restricts whether implementations can be `constexpr`-friendly as an extension. Instead, it seems better to just say "if".

Finally, I think the condition under which the function must be `constexpr`-friendly is not something we can test for because it says "value-initialization of the alternative type `T0` would satisfy the requirements for a constexpr function", which doesn't imply the value initialization can actually be be performed inside a constexpr context (for example the default constructor could be `constexpr` friendly but not marked with the `constexpr` keyword).

[2017-06-17, Tim Song comments]

This issue is related to LWG 2833(i).

[2019-07 Issue Prioritization]

Priority to 2 after discussion on the reflector.

Previous resolution [SUPERSEDED]

This wording is relative to N4810.

1. Modify 22.6.3.2 [variant.ctor] as indicated:

   >     constexpr variant() noexcept(see below);

   > > -2- Effects: […]

   > > -3- Ensures: […]

   > > -4- Throws: […]

   > > -5- Remarks: This function shall be usable in a context that requires constant evaluation if the alternative type `T0` can be value-initialized in a context that requires constant evaluation `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a constexpr function. […]

[2020-06-08 Nina Dinka Ranns comments]

The revised wording provided by LWG 2833(i) should resolve this issue as well.

Proposed resolution:

### 3219(i). `std::array` overview container requirements are incorrect

Section: 23.3.3.1 [array.overview], 23.2.2 [container.requirements.general] Status: New Submitter: Nevin Liber & Christian Trott Opened: 2019-06-13 Last modified: 2022-04-24

Priority: 3

View other active issues in [array.overview].

View all other issues in [array.overview].

View all issues with New status.

Discussion:

The requirements specified in 23.3.3.1 [array.overview] p3 are incorrect; namely:

- A default constructed `array<T, N>` where `0 < N` has linear, not constant complexity.

- A default constructed `array<T, 0>` is empty and has constant complexity.

[2019-07 Issue Prioritization]

Priority to 3 after discussion on the reflector.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4810.

> 1. Modify 23.2.2 [container.requirements.general], Table 62 — "Container requirements", as indicated (This table can be identified by the "section" identifier [tab:container.req] in the next working draft):

>    > Table 62 — Container requirements
>    > Expression | Return type | Operational semantics | Assertion/note pre/post-condition | Complexity
>    > --- | --- | --- | --- | ---
>    > `[…]`
>    > `X u;` | `` |  | Ensures: `!u.empty()` for `array<T, N>` where `0 < N`, and Ensures: `u.empty()` for all other standard containers. | constant(Note A)
>    > `X()` | `` |  | Ensures: `!X().empty()` for `array<T, N>` where `0 < N`, and Ensures: `X().empty()` for all other standard containers. | constant(Note A)
>    > `[…]`

>    > Those entries marked "(Note A)" or "(Note B)" have linear complexity for `array<T, N>` where `0 < N` and have constant complexity for all other standard containers.

> 2. Modify 23.3.3.1 [array.overview] as indicated:

>    > -2- An `array` is an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up to `N` elements whose types are convertible to `T`.

>    > -3- An `array<T, 0>` satisfies all of the requirements of a container and of a reversible container (23.2 [container.requirements]). An `array<T, N>` where `0 < N` satisfies all of the requirements of a container and of a reversible container (23.2 [container.requirements]), except that a default constructed `array<T, N>` object is not empty and thatboth default construction and `swap` does not have constantlinear complexity. An `array` satisfies some of the requirements of a sequence container (23.2.4 [sequence.reqmts]). Descriptions are provided here only for operations on `array` that are not described in one of these tables and for operations where there is additional semantic information.

[2022-04-24; Daniel rebases wording on N4910]

Proposed resolution:

This wording is relative to N4910.

1. Modify 23.2.2.2 [container.reqmts] as indicated:

   >     X u;
   >     X u = X();

   > > -10- Postconditions: `!u.empty()` for `array<T, N>` where `0 < N`, and `u.empty()` for all other standard containers.

   > > -11- Complexity: ConstantLinear for `array<T, N>` where `0 < N` and constant for all other standard containers.

   > […]

   >     X u(rv);
   >     X u = rv;

   > > -15- Postconditions: `u` is equal to the value that `rv` had before this construction.

   > > -11- Complexity: Linear for `array<T, N>` where `0 < N` and constant for all other standard containers.

   > […]

   >     a.swap(b)

   > > -45- Result: `void`

   > > -46- Effects: Exchanges the contents of `a` and `b`.

   > > -47- Complexity: Linear for `array<T, N>` where `0 < N` and constant for all other standard containers.

2. Modify 23.3.3.1 [array.overview] as indicated:

   > -2- An `array` is an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up to `N` elements whose types are convertible to `T`.

   > -3- An `array<T, 0>` meets all of the requirements of a container (23.2.2.2 [container.reqmts]) and of a reversible container (23.2.2.3 [container.rev.reqmts]). An `array<T, N>` where `0 < N` meets all of the requirements of a container (23.2.2.2 [container.reqmts]) and of a reversible container (23.2.2.3 [container.rev.reqmts]), except that a default constructed `array<T, N>` object is not empty if `N > 0` and default construction, move construction, and `swap` have linear complexity if `N > 0`. An `array` meets some of the requirements of a sequence container (23.2.4 [sequence.reqmts]). Descriptions are provided here only for operations on `array` that are not described in one of these tables and for operations where there is additional semantic information.

### 3227(i). Ambiguity issue for `extract` in ordered and unordered associative containers

Section: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: New Submitter: Konstantin Boyarinov Opened: 2019-06-25 Last modified: 2026-06-09

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with New status.

Discussion:

Ordered and unordered associative containers in C++14 contained an issue, which caused an ambiguity while invoking `std::map::erase` when `key_type` of the map can be constructed from the iterator. In this case both overloads `erase(const key_type&)` and `erase(const_iterator)` could be chosen.

The issue LWG 2059(i) was reported and resolved in C++17 by adding an extra overload for `erase` in ordered and unordered associative containers which accepts `iterator` as an argument.

C++17 also introduced new functionality for splicing ordered and unordered maps and sets. One of the extensions allows to extract a node from the container by passing either `key_type&` or `const_iterator` to the `extract()` member function:

>     node_type extract(const key_type& x);
>     node_type extract(const_iterator position);

Providing these two `extract` overloads causes the same problem as for `erase`. Consider the following example:

>     #include <map>
>     #include <string>

>     struct Key
>     {
>       template <typename T>
>       Key(const T&) {}
>     };

>     bool operator<(const Key&, const Key&) { return false; }

>     int main()
>     {
>       using map_type = std::map<Key, std::string>;

>       map_type m({ {Key(1), "a"}, {Key(2), "b"} });
>       map_type::iterator it = m.begin();
>       auto nh = m.extract(it);
>     }

In this case, call to `extract()` is ambiguous, because the overloads which accept `const_iterator` and `key_type` are equally good matches for the argument `it`.

Consequently, this issue can be resolved in the same way as for `std::map::erase` by adding an overload for `extract` which accepts `iterator` as an argument.

[2019-07 Issue Prioritization]

Priority to 3 after discussion on the reflector.

Previous resolution [SUPERSEDED]

This wording is relative to N4820.

1. Modify [tab:container.assoc.req], Table 69 — "Associative container requirements", as indicated:

   > Table 69 — Associative container requirements (in addition to container) [tab:container.assoc.req]
   > Expression | Return type | Assertion/note pre-/post-condition | Complexity
   > --- | --- | --- | ---
   > `…`
   > `a.extract(q)` | `node_type` | Effects: Removes the element pointed to by `q`. Returns: A `node_type` owning that element. | amortized constant
   > `a.extract(r)` | `node_type` | Effects: Removes the element pointed to by `r`. Returns: A `node_type` owning that element. | amortized constant
   > `…`

2. Modify [tab:container.assoc.req], Table 70 — "Unordered associative container requirements", as indicated:

   > Table 70 — Unordered associative container requirements (in addition to container) [tab:container.hash.req]
   > Expression | Return type | Assertion/note pre-/post-condition | Complexity
   > --- | --- | --- | ---
   > `…`
   > `a.extract(q)` | `node_type` | Effects: Removes the element pointed to by `q`. Returns: A `node_type` owning that element. | Average case `𝒪(1)`, worst case `𝒪(a.size())`.
   > `a.extract(r)` | `node_type` | Effects: Removes the element pointed to by `r`. Returns: A `node_type` owning that element. | Average case `𝒪(1)`, worst case `𝒪(a.size())`.
   > `…`

3. Modify 23.4.3.1 [map.overview], class template `map` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

4. Modify 23.4.4.1 [multimap.overview], class template `multimap` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

5. Modify 23.4.6.1 [set.overview], class template `set` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

6. Modify 23.4.7.1 [multiset.overview], class template `multiset` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

7. Modify 23.5.3.1 [unord.map.overview], class template `unordered_map` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

8. Modify 23.5.4.1 [unord.multimap.overview], class template `unordered_multimap` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

9. Modify 23.5.6.1 [unord.set.overview], class template `unordered_set` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

10. Modify 23.5.7.1 [unord.multiset.overview], class template `unordered_multiset` synopsis, as indicated:

    >     […]
    >     node_type extract(iterator position);
    >     node_type extract(const_iterator position);
    >     node_type extract(const key_type& x);
    >     […]

[2022-04-24; Daniel rebases wording on N4910]

Proposed resolution:

This wording is relative to N4910.

1. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   >     a.extract(q)

   > > -108- Result: `node_type`

   > > -109- Effects: Removes the element pointed to by `q`.

   > > -110- Returns: A `node_type` owning that element.

   > > -111- Complexity: Amortized constant.

   >     a.extract(r)

   > > -?- Result: `node_type`

   > > -?- Effects: Removes the element pointed to by `r`.

   > > -?- Returns: A `node_type` owning that element.

   > > -?- Complexity: Amortized constant.

2. Modify 23.2.8.1 [unord.req.general] as indicated:

   >     a.extract(q)

   > > -141- Result: `node_type`

   > > -142- Effects: Removes the element pointed to by `q`.

   > > -143- Returns: A `node_type` owning that element.

   > > -144- Complexity: Average case `𝒪(1)`, worst case `𝒪(a.size())`.

   >     a.extract(r)

   > > -?- Result: `node_type`

   > > -?- Effects: Removes the element pointed to by `r`.

   > > -?- Returns: A `node_type` owning that element.

   > > -?- Complexity: Average case `𝒪(1)`, worst case `𝒪(a.size())`.

3. Modify 23.4.3.1 [map.overview], class template `map` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

4. Modify 23.4.4.1 [multimap.overview], class template `multimap` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

5. Modify 23.4.6.1 [set.overview], class template `set` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

6. Modify 23.4.7.1 [multiset.overview], class template `multiset` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

7. Modify 23.5.3.1 [unord.map.overview], class template `unordered_map` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

8. Modify 23.5.4.1 [unord.multimap.overview], class template `unordered_multimap` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

9. Modify 23.5.6.1 [unord.set.overview], class template `unordered_set` synopsis, as indicated:

   >     […]
   >     node_type extract(iterator position);
   >     node_type extract(const_iterator position);
   >     node_type extract(const key_type& x);
   >     […]

10. Modify 23.5.7.1 [unord.multiset.overview], class template `unordered_multiset` synopsis, as indicated:

    >     […]
    >     node_type extract(iterator position);
    >     node_type extract(const_iterator position);
    >     node_type extract(const key_type& x);
    >     […]

### 3229(i). §[res.on.exception.handling]#3 cannot apply to types with implicitly declared destructors

Section: 16.4.6.14 [res.on.exception.handling] Status: New Submitter: Nevin Liber Opened: 2019-06-28 Last modified: 2023-01-29

Priority: 3

View other active issues in [res.on.exception.handling].

View all other issues in [res.on.exception.handling].

View all issues with New status.

Discussion:

16.4.6.14 [res.on.exception.handling]#3 says:

> Destructor operations defined in the C++ standard library shall not throw exceptions. Every destructor in the C++ standard library shall behave as if it had a non-throwing exception specification.

However, types like `pair` and `array` have implicitly declared destructors, where 14.5 [except.spec]#8 applies:

> The exception specification for an implicitly-declared destructor, or a destructor without a noexcept-specifier, is potentially-throwing if and only if any of the destructors for any of its potentially constructed subobjects is potentially-throwing or the destructor is virtual and the destructor of any virtual base class is potentially throwing.

We can relax 16.4.6.14 [res.on.exception.handling] to only cover explicitly declared destructors because if they hold a user type where the destructor throws then we get UB from 16.4.5.8 [res.on.functions]#2:

> In particular, the effects are undefined in the following cases: […]

> 1. […]

> 2. (2.4) — if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.

> 3. […]

and the referred to UB happens before [res.on.exception.handling] could apply.

[2019-07 Issue Prioritization]

Priority to 3 after discussion on the reflector.

[2023-01-29; Daniel comments]

This issue has very much overlap with LWG 3854(i).

Proposed resolution:

This wording is relative to N4820.

1. Modify 16.4.6.14 [res.on.exception.handling] as indicated:

   > -3- Destructor operations defined in the C++ standard library shall not throw exceptions. Every explicitly declared destructor in the C++ standard library shall behave as if it had a non-throwing exception specification.

### 3240(i). Headers declare more than entities

Section: 16.4.3.2 [using.headers] Status: New Submitter: Alisdair Meredith Opened: 2019-07-24 Last modified: 2020-04-07

Priority: 3

View all other issues in [using.headers].

View all issues with New status.

Discussion:

Quoting 16.4.3.2 [using.headers] p3:

> "[…] and shall include the header lexically before the first reference in that translation unit to any of the entities declared in that header."

This suggests we may be able to use macros and typedefs (like `size_t`) declared in standard headers without the corresponding `#include`. Clearly that is not the intended behavior!

I thought about replacing 'entity' with 'name', but the same name may occur with different contexts throughout the standard library. I thought about "contents of that header", but run afoul of the same function overloads (`std::begin` etc.) being declared in multiple headers.

It may be simpler to turn this sentence around, along the lines of:

> "No part of the standard library shall be used in a translation unit prior to a including or importing a header that provides that feature."

Even here, 'used' may be a problematic term of art. Perhaps "named"?

[2020-04-07 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3261(i). `regex` components' `noexcept` annotations appear broken for POCMA or throwing `BidirectionalIterator`

Section: 28.6.7 [re.regex], 28.6.9 [re.results] Status: New Submitter: Billy O'Neal III Opened: 2019-08-17 Last modified: 2019-10-07

Priority: 3

View all other issues in [re.regex].

View all issues with New status.

Discussion:

`std::basic_regex` and `std::match_results` have `noexcept` move construction, and `std::basic_regex` has `noexcept` move assignment, but both of them have throwing swaps. We probably need an Expects: or something to say that `BidirectionalIterator` doesn't throw through these operations. We probably also need `match_results::operator=` to respect `propagate_on_container_move_assignment` (and maybe the copy ctor respect `propagate_on_container_copy_assignment`).

[2019-09-02; Tim Song comments]

The issue is related to LWG 2490(i).

[2019-10 Priority set to 3 after reflector discussion]

Proposed resolution:

### 3263(i). Atomic waiting function calls should only be unblocked once

Section: 32.5.6 [atomics.wait] Status: New Submitter: Geoffrey Romer Opened: 2019-08-19 Last modified: 2020-09-06

Priority: 3

View other active issues in [atomics.wait].

View all other issues in [atomics.wait].

View all issues with New status.

Discussion:

It appears that in a conforming implementation, all but one `wait()` call on a given atomic object may block forever, regardless of any `notify_one()` calls, because in principle every `notify_one()` call could be considered to unblock the same single `wait()` call. Common sense suggests (and David Olsen confirms) that the intent is for each waiting function call to be (non-spuriously) unblocked by at most one notifying function call, but as far as I can tell the words never say that.

[2019-09-14 Priority set to 3 based on reflector discussion]

Proposed resolution:

This wording is relative to N4830.

1. Modify 32.5.6 [atomics.wait] as indicated:

   > -?- All blocking and unblocking events on a single atomic object occur in a single total order that is consistent with the "happens before" partial order.

   > -4- A call to an atomic waiting operation on an atomic object `M` is eligible to be unblocked by a call to an atomic notifying operation on `M` if it has not been unblocked, and there exist side effects `X` and `Y` on `M` such that:

   > 1. (4.1) — the atomic waiting operation has blocked after observing the result of `X`,

   > 2. (4.2) — `X` precedes `Y` in the modification order of `M`, and

   > 3. (4.3) — `Y` happens before the call to the atomic notifying operation.

### 3267(i). Rebound allocators and `is_always_equal`

Section: 16.4.4.6 [allocator.requirements] Status: New Submitter: FrankHB1989 Opened: 2019-08-27 Last modified: 2023-01-14

Priority: 4

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

[allocator.requirements] does not mention the interaction between `is_always_equal` and allocator rebinding. As the result, a rebound allocator may have different `is_always_equal::value` to the original allocator.

Further, for an allocator type `X` satisfying `std::allocator_type<X>::is_always_equal::value == true`, rebound allocators of `X` with same type are not guaranteed equal.

Consider:

1. `X` is used as an allocator for `value_type` used in a node-based container;

2. `Y` is the rebound allocator type for the node type used in the implementation;

3. `b1` and `b2` are values of `Y` from different allocator objects.

Then, `std::allocator_type<X>::is_always_equal::value == true` does not necessarily imply `b1 == b2`.

Since some of containers in the standard have already explicitly relied on `is_always_equal` of allocators for their `value_type` (notably, in the exception specification of the move assignment), this can cause subtle problems.

In general, the implementation of the move assignment operator of such a container can not avoid allocation for new nodes when `!std::allocator_traits<Y>::propagate_on_container_move_assignment::value && b1 != b2`. This can throw, and it can clash with the required exception specification based on `std::allocator_traits<value_type>::is_always_equal`:

>     #include <utility>
>     #include <memory>
>     #include <new>
>     #include <map>
>     #include <functional>
>     #include <type_traits>

>     using K = int;
>     using V = int;
>     using P = std::pair<const K, V>;

>     bool stop_alloc;

>     template<typename T>
>     struct AT
>     {
>       using value_type = T;

>       std::shared_ptr<void> sp = {};

>       template<typename U>
>       struct rebind
>       {
>         using other = AT<U>;
>       };

>       using is_always_equal = std::is_same<T, P>;

>       AT() : sp(is_always_equal::value ? nullptr : new T*()) {}

>       AT(const AT& a) = default;

>       template<typename U>
>       AT(const AT<U>& a) noexcept : sp(a.sp) {}

>       T* allocate(std::size_t size)
>       {
>         if (stop_alloc)
>           throw std::bad_alloc();
>         return static_cast<T*>(::operator new(size * sizeof(T)));
>       }

>       void deallocate(T* p, std::size_t)
>       {
>         ::operator delete(p);
>       }

>       friend bool operator==(const AT& x, const AT& y) noexcept
>       {
>         return !x.sp.owner_before(y.sp) && !y.sp.owner_before(x.sp);
>       }

>       friend bool operator!=(const AT& x, const AT& y) noexcept
>       {
>         return !(x == y);
>       }

>     };

>     using A = AT<P>;

>     int main()
>     {
>       // Some sanity checks:
>       static_assert(std::is_same_v<A::template rebind<A::value_type>::other, A>);
>       // For any U:
>       using U = int;
>       static_assert(std::is_same_v<A::template rebind<U>::other::template rebind<A::value_type>::other, A>);

>       using C = std::less<>;
>       using M = std::map<K, V, C, A>;

>       // As required by the current wording of the container move operator:
>       using always_equal = std::allocator_traits<A>::is_always_equal;
>       constexpr bool std_nothrow = always_equal::value && std::is_nothrow_move_assignable_v<C>;
>       static_assert(std_nothrow);

>       // For conforming implementations:
>       // static_assert(!(std_nothrow && !std::is_nothrow_move_assignable<M>::value));

>       M m{{K(), V()}}, m2;
>       auto a = m.get_allocator();

>       a.sp = std::make_shared<int>(42);
>       stop_alloc = true;

>       try
>       {
>         // Call terminate with conforming implementations. This does not work on libstdc++.
>         m2 = std::move(m);
>         // For libstdc++, terminate on allocator-extended move constructor call.
>         //    M m3(std::move(m), a);
>       }
>       catch(...)
>       {}
>     }

[2019-10 Priority set to 4 after reflector discussion]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4830.

> > [Drafting note: Additional questions: Is it necessary to ensure that
> > `XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::value` is `true` as well?]

> 1. Modify 16.4.4.6 [allocator.requirements], Table [tab:cpp17.allocator] "`Cpp17Allocator` requirements" as indicated:

>    > Table 34 — `Cpp17Allocator` requirements [tab:cpp17.allocator]
>    > Expression | Return type | Assertion/note pre-/post-condition | Default
>    > --- | --- | --- | ---
>    > `…`
>    > `typename X::template rebind<U>::other` | `Y` | For all `U` (including `T`), `Y::template rebind<T>::other` is `X`. `XX::is_always_equal::value == YY::is_always_equal::value` is `true`. | See Note A, below.
>    > `…`

[2022-04-24; Daniel rebases wording on N4910]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4910.

> > [Drafting note: Additional questions: Is it necessary to ensure that
> > `XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::value` is `true` as well?]

> 1. Modify 16.4.4.6 [allocator.requirements] as indicated:

>    >     typename X::template rebind<U>::other

>    > > -16- Result: `Y`

>    > > -17- Postconditions: For all `U` (including `T`), `Y::template rebind<T>::other` is `X`. `XX::is_always_equal::value == YY::is_always_equal::value` is `true`.

>    > > -18- Remarks: If `Allocator` is a class template instantiation of the form `SomeAllocator<T, Args>`, where `Args` is zero or more type arguments, and `Allocator` does not supply a `rebind` member template, the standard `allocator_traits` template uses `SomeAllocator<U, Args>` in place of `Allocator::rebind<U>::other` by default. For allocator types that are not template instantiations of the above form, no default is provided.

>    > > -19- [Note 1: The member class template `rebind` of `X` is effectively a typedef template. In general, if the name `Allocator` is bound to `SomeAllocator<T>`, then `Allocator::rebind<U>::other` is the same type as `SomeAllocator<U>`, where `SomeAllocator<T>::value_type` is `T` and `SomeAllocator<U>::value_type` is `U`. — end note]

[2023-01-08; Jiang An comments and provides improved wording]

Exception specifications of some container operations (added by N4258 and LWG 3778(i)) are specified with the propagation properties of template parameter `Allocator`. However, for node-based containers and `std::deque` (and common implementations of `std::vector<bool, A>`), rebound allocators are needed to be propagated, and common implementations are currently detecting the propagation properties of rebound allocators.

I think if the allocator provided as template argument and the rebound have different propagation properties and behaves differently on propagation, then it is difficult or impossible for implementations to follow the current exception specifications.

Proposed resolution:

This wording is relative to N4917.

1. Modify 16.4.4.6 [allocator.requirements] as indicated:

   >     typename X::template rebind<U>::other

   > > -16- Result: `Y`

   > > -17- Postconditions: For all `U` (including `T`), `Y::template rebind<T>::other` is `X`. All of `XX::is_always_equal::value == YY::is_always_equal::value`, `XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::value`, `XX::propagate_on_container_move_assignment::value == YY::propagate_on_container_move_assignment::value`, and `XX::propagate_on_container_swap::value == YY::propagate_on_container_swap::value` are `true`.

   > > -18- Remarks: If `Allocator` is a class template instantiation of the form `SomeAllocator<T, Args>`, where `Args` is zero or more type arguments, and `Allocator` does not supply a `rebind` member template, the standard `allocator_traits` template uses `SomeAllocator<U, Args>` in place of `Allocator::rebind<U>::other` by default. For allocator types that are not template instantiations of the above form, no default is provided.

   > > -19- [Note 1: The member class template `rebind` of `X` is effectively a typedef template. In general, if the name `Allocator` is bound to `SomeAllocator<T>`, then `Allocator::rebind<U>::other` is the same type as `SomeAllocator<U>`, where `SomeAllocator<T>::value_type` is `T` and `SomeAllocator<U>::value_type` is `U`. — end note]

### 3275(i). Why does `time_get::do_get` require a valid pointer when none of the others do?

Section: 28.3.4.6.2.3 [locale.time.get.virtuals] Status: New Submitter: Marshall Clow Opened: 2019-09-09 Last modified: 2020-09-06

Priority: 3

View other active issues in [locale.time.get.virtuals].

View all other issues in [locale.time.get.virtuals].

View all issues with New status.

Discussion:

According to 28.3.4.6.2.3 [locale.time.get.virtuals] p11:

> Requires: `t` shall point to an object

[Note: In my "Mandates" paper, I changed this to "Expects: `t` points to an object"]

Nevertheless, it's odd, and inconsistent. `time_get::get` does not have any such stated requirement, and it calls `do_get`. None of the other "time" calls in `time_get` have such a (stated) requirement.

I believe that this requirement is redundant, that it is implied by the wording in P12 and P14.

P12: "or until it has extracted and assigned those `struct tm` members"

P14: "It is unspecified whether multiple calls to `do_get()` with the address of the same `struct tm` object will update the current contents of the object or simply overwrite its members."

If the pointer is invalid (null, or points to unmapped memory, say), you've got UB anyway.

All the other calls in [locale.time.get.virtuals] were from C++98. `do_get_time` was added in C++11, and p11 originally said "`t` shall be dereferenceable".

This was changed to "`t` shall point to an object" as part of the resolution of CWG issue 342

[2019-10 Priority set to 3 after reflector discussion]

Proposed resolution:

This wording is relative to N4830.

1. Modify 28.3.4.6.2.3 [locale.time.get.virtuals] as indicated:

   >     iter_type do_get(iter_type s, iter_type end, ios_base& f,
   >                      ios_base::iostate& err, tm* t, char format, char modifier) const;

   > > -11- Requires: `t` shall point to an object.

   > > -12- Effects: […]

### 3287(i). Exposition-only `cpp17-input-iterator` concept is needlessly complex

Section: 24.3.2.3 [iterator.traits] Status: New Submitter: Eric Niebler Opened: 2019-09-10 Last modified: 2020-09-06

Priority: 3

View all other issues in [iterator.traits].

View all issues with New status.

Discussion:

The new C++20 iterator concepts use `common_reference` to constrain the value, reference, and rvalue_reference associated types in order to support proxy references (see 24.3.4.2 [iterator.concept.readable]).

However, the C++17 iterators did not support proxy references, so the use of `common_reference` in 24.3.2.3 [iterator.traits]/p2 is needlessly complex. The `common_reference` constraints can be replaced with simple convertibility requirements to a `const` lvalue reference to the value type.

This fix has been implemented in range-v3.

[2019-10-14 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

This wording is relative to N4830.

1. Modify 24.3.2.3 [iterator.traits] as indicated:

   > -2- The definitions in this subclause make use of the following exposition-only concepts:

   > >     template<class I>
   > >     concept cpp17-iterator =
   > >       copyable<I> && requires(I i) {
   > >         {   *i } -> can-reference;
   > >         {  ++i } -> same_as<I&>;
   > >         { *i++ } -> can-reference;
   > >       };

   > >     template<class I>
   > >     concept cpp17-input-iterator =
   > >       cpp17-iterator<I> && equality_comparable<I> && requires(I i) {
   > >         typename incrementable_traits<I>::difference_type;
   > >         typename readable_traits<I>::value_type;
   > >         typename common_reference_t<iter_reference_t<I>&&,
   > >                                     typename readable_traits<I>::value_type&>;
   > >         typename common_reference_t<decltype(*i++)&&,
   > >                                     typename readable_traits<I>::value_type&>;
   > >         { *i } -> convertible_to<const typename readable_traits<I>::value_type&>;
   > >         { *i++ } -> convertible_to<const typename readable_traits<I>::value_type&>;
   > >         requires signed_integral<typename incrementable_traits<I>::difference_type>;
   > >       };

   > >     […]

### 3288(i). `atomic<T>::notify_one` is unimplementable

Section: 32.5.6 [atomics.wait] Status: New Submitter: Anthony Williams Opened: 2019-09-11 Last modified: 2020-09-06

Priority: 2

View other active issues in [atomics.wait].

View all other issues in [atomics.wait].

View all issues with New status.

Discussion:

I am concerned by the wording around `atomic<T>::wait()/atomic<T>::notify_one()`.

32.5.6 [atomics.wait] p4 requires that the thread that calls `wait()` observed a value `X` prior to the value `Y` which results from a store that happens-before the notify in order to be eligible to be unlocked.

I am not sure how to implement that.

>     atomic<int> a = 0;

>     T1: int ra=a, read 0
>     T1: a.wait(0)
>     T2: a=42
>     T3: int ra=a, read 42
>     T3: a.wait(42)
>     T2: a.notify_one()

The wording requires that `T1` is eligible to be unlocked, but not `T3`, as there is not a write after the value read by `T3` that happens-before the notify.

However, both `T1` and `T3` are waiting, so `T3` may be woken by the OS. Waking `T3` is allowed (`wait()` says it may wake spuriously), but waking `T1` is currently required as it is the only thread "eligible to be unblocked".

This requires `notify_one()` to wake all waiters, which defeats the purpose.

I suspect we need to change 32.5.6 [atomics.wait] p4.

How about:

> "A call to an atomic waiting operation `W` on an atomic object `M` is eligible to be unlocked by a call to an atomic notifying operation `N` on `M` if

> - `N` does not happen-before `W`

> - There are no side effects `X` and `Y` in the modification order of `M` such that `N` happens-before `X`, `X` precedes `Y` in the modification order of `M` and an atomic operation that observes the effects of `Y` happens-before `W`.

> "

This would allow `T3` to be woken in the preceding example, but prevent it being woken in the following case:

>     T1: int ra=a, read 0
>     T1: a.wait(0)
>     T2: a=42
>     T2: a.notify_one()
>     T2: a=69
>     T3: int ra=a, read 69
>     T3: a.wait(69)

[2020-07-17; Priority set to 2 in telecon]

Proposed resolution:

This wording is relative to N4830.

1. Modify 32.5.6 [atomics.wait] as indicated:

   > -4- A call to an atomic waiting operation `W` on an atomic object `M` is eligible to be unblocked by a call to an atomic notifying operation `N` on `M` if there exist side effects `X` and `Y` on `M` such that:

   > 1. (4.1) — `N` does not happen before `W` the atomic waiting operation has blocked after observing the result of `X`,

   > 2. (4.2) — There are no side effects `X` andprecedes `Y` in the modification order of `M`, andsuch that `N` happens before `X`, `X` precedes `Y` in the modification order of `M` and an atomic operation that observes the effects of `Y` happens before `W`.

   > 3. (4.3) — `Y` happens before the call to the atomic notifying operation.

### 3297(i). Useless sequence container requirement

Section: 23.2.4 [sequence.reqmts] Status: New Submitter: Casey Carter Opened: 2019-09-17 Last modified: 2022-04-24

Priority: 3

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with New status.

Discussion:

23.2.4 [sequence.reqmts] paragraph 3 says that the names `i` and `j` denote "iterators that meet the Cpp17InputIterator requirements and refer to elements implicitly convertible to `value_type`". Ignoring for the moment that this is an occurrence of LWG 3105(i) — we really mean that `*i` and `*j` must be implicitly convertible to `value_type` — this requirement seems to be completely extraneous.

The names `i` and `j` are used in three places in the requirements table:

- The range constructors `X(i, j)` and `X u(i, j)`, which require that the container's value type is Cpp17EmplaceConstructible into the container from `*i`; implicit conversion is neither necessary nor sufficient.

- The range insert overload `a.insert(p, i, j)` which also requires Cpp17EmplaceConstructible, as well as the capability to move elements around for `vector` and `deque`; again, implicit conversion is neither necessary nor sufficient. It would be useful / performant here to require that the container's value type is assignable from `*i`, which may have been the intent of the implicit conversion requirement — would doing so be too breaking?

- The range assign overload `a.assign(i, j)` which requires both Cpp17EmplaceConstructible as above and that it can assign the result of dereferencing an iterator directly to the container's value type; again, implicit conversion is not useful here.

We should strike the implicit conversion requirement since it is not useful and only serves to confuse readers of the Standard (see e.g. here).

[2019-10-31 Issue Prioritization]

Priority to 3 after reflector discussion.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4830.

> 1. Modify 23.2.4 [sequence.reqmts] as indicated:

>    > -3- In Tables 76 and 77, `X` denotes a sequence container class, `a` denotes a value of type `X` containing elements of type `T`, `u` denotes the name of a variable being declared, `A` denotes `X::allocator_type` if the qualified-id `X::allocator_type` is valid and denotes a type (13.10.3 [temp.deduct]) and `allocator<T>` if it doesn't, `i` and `j` denote iterators that meet the Cpp17InputIterator requirements and refer to elements implicitly convertible to `value_type`, `[i, j)` denotes a valid range, […]

[2022-04-24; Daniel rebases wording on N4910]

Proposed resolution:

This wording is relative to N4910.

1. Modify 23.2.4 [sequence.reqmts] as indicated:

   > -3- In this subclause,

   > 1. (3.1) — […]

   > 2. […]

   > 3. (3.5) — `i` and `j` denote iterators that meet the Cpp17InputIterator requirements and refer to elements implicitly convertible to `value_type`,

   > 4. […]

### 3308(i). `vector` and `deque` iterator `erase` invalidates elements even when no change occurs

Section: 23.3.5.4 [deque.modifiers], 23.3.13.5 [vector.modifiers] Status: New Submitter: Billy O'Neal III Opened: 2019-10-29 Last modified: 2019-11-04

Priority: 3

View other active issues in [deque.modifiers].

View all other issues in [deque.modifiers].

View all issues with New status.

Discussion:

It seems incorrect that a container would invalidate anything as a result of being asked to erase 0 elements. This came up in a recent customer bug report against Visual Studio, where given a `vector v`, `v.erase(v.begin(), v.begin())` triggered a self-assignment of all the elements in the vector.

`deque` has language enumerating erasures of the first and last element which invalidate fewer iterators, and a fallback that says all iterators are invalidated, which seems to intend to be talking about middle-of-container erasures. However, erasing 0 elements isn't really a middle of container erasure.

`vector` says that iterators and references are invalidated after the 'point of the erase', but when 0 elements are erased it's unclear what that even means.

We should say that erasures that erase 0 elements are no ops and be clearer about which elements are invalidated for `vector`.

[2019-11 Priority to 3 during Monday issue prioritization in Belfast]

Proposed resolution:

This wording is relative to N4835.

1. Modify 23.3.5.4 [deque.modifiers] as indicated:

   >     iterator erase(const_iterator position);
   >     iterator erase(const_iterator first, const_iterator last);
   >     void pop_front();
   >     void pop_back();

   > > -4- Effects: Erases elements as indicated in Table 75 [tab:container.seq.req]. An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases any elements, but neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque. [Note: `pop_front` and `pop_back` are erase operations. — end note]

   > > […]

2. Modify 23.3.13.5 [vector.modifiers] as indicated:

   >     constexpr iterator erase(const_iterator position);
   >     constexpr iterator erase(const_iterator first, const_iterator last);
   >     constexpr void pop_back();

   > > -3- Effects: Erases elements as indicated in Table 75 [tab:container.seq.req]. Invalidates iterators and references at or after the point of the erase.first erased element. [Note: For the second overload of `erase`, if `first == last`, no elements are erased, and no iterators or references are invalidated. — end note]

   > > […]

### 3309(i). Is `<ios>` implicitly `#include`d by `<sstream>`, `<fstream>` etc.?

Section: 31.8 [string.streams], 31.10 [file.streams] Status: New Submitter: Jens Maurer Opened: 2019-11-01 Last modified: 2019-11-04

Priority: 3

View all other issues in [string.streams].

View all issues with New status.

Discussion:

It is unclear whether the streams headers implicitly `#include <ios>` and make (for example) the name `std::basic_ios` available after including `<sstream>`.

This question becomes more important as header units arrive with modules, where there is an explicit choice whether to re-export names from subordinate headers.

[2019-11 Priority to 3 during Monday issue prioritization in Belfast]

Proposed resolution:

### 3337(i). What is "is initialized" supposed to mean?

Section: 28.3.4.2.5.3 [locale.codecvt.virtuals] Status: New Submitter: Richard Smith Opened: 2019-11-15 Last modified: 2019-11-30

Priority: 3

View other active issues in [locale.codecvt.virtuals].

View all other issues in [locale.codecvt.virtuals].

View all issues with New status.

Discussion:

In 28.3.4.2.5.3 [locale.codecvt.virtuals] paragraphs 6 and 11, we find:

> Preconditions: `(to <= to_end)` is well-defined and `true`; `state` is initialized, if at the beginning of a sequence, or else is equal to the result of converting the preceding characters in the sequence."

This doesn't make sense. What is the value of `state` if we're at the beginning of a sequence? Is the fact that we say that it's initialized in that case supposed to imply that it need not be initialized otherwise?

Perhaps this means "value-initialized" or "default-initialized" instead of merely "initialized"?

[2019-11-30 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3339(i). Move-constructed empty-container capacity

Section: 27.4.3 [basic.string], 23.3.13 [vector], 23.5.3 [unord.map], 23.5.6 [unord.set], 23.2.2 [container.requirements.general] Status: New Submitter: Nathan Myers Opened: 2019-11-17 Last modified: 2020-09-06

Priority: 3

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with New status.

Discussion:

The Standard leaves unspecified the `capacity()` of a `string` or `vector`, and `bucket_count()` of an `unordered_(multi)set` or `unordered_(multi)map`, constructed by move from an empty `other`.

For a non-empty `other`, normative language in the Standard constrains the new object to use (mostly) the same storage as the other, by way of lifetime of iterators and pointers to elements.

For an empty `other`, there can be no such pointers or iterators. However, the empty container may have a non-zero `capacity()` or `bucket_count()`, and having reserved storage there, one naturally expects that storage to be delivered to the new object in the same way as if it had elements.

Existing implementations, in fact, do move storage to the new container, provided it can be deallocated using the new object's allocator. It is likely that existing programs have come to depend on this behavior.

The resolution proposed is to add language to the Standard specifying that, if the allocators of the existing and new container objects are compatible, the storage of the new object is the same as of the old, so that no allocations or deallocations are performed in the process, as existing implementations in fact do.

This appears to affect only `string`, `vector`, `unordered_set`, `unordered_multiset`, `unordered_map`, and `unordered_multimap`, but any new container types may also need similar attention.

Note that in the case of the hashed containers, the array of buckets appears not to be required to be moved, even when elements contained are. This seems to be a similar oversight; extant implementations do move the bucket array. The resolution should cover this case as well.

It is expected and intended that the proposed resolution does not require changes to the behavior of implementations.

See also LWG 2321(i) and P0966R1.

[2019-11-30 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3341(i). `basic_regex` range constructor: Missing requirements for iterator types

Section: 28.6.7.2 [re.regex.construct] Status: New Submitter: Денис Захаров Opened: 2019-11-17 Last modified: 2022-04-22

Priority: 3

View other active issues in [re.regex.construct].

View all other issues in [re.regex.construct].

View all issues with New status.

Discussion:

This is description of a `basic_regex` range constructor from N4835, 28.6.7.2 [re.regex.construct]:

>     template<class ForwardIterator>
>       basic_regex(ForwardIterator first, ForwardIterator last,
>                   flag_type f = regex_constants::ECMAScript);

> > -17- Throws: `regex_error` if the sequence `[first, last)` is not a valid regular expression.

> > -18- Effects: Constructs an object of class `basic_regex`; the object's internal finite state machine is constructed from the regular expression contained in the sequence of characters `[first, last)`, and interpreted according to the flags specified in `f`.

> > -19- Ensures: `flags()` returns `f. mark_count()` returns the number of marked sub-expressions within the expression.

It seems that there are no requirements about dereferenced iterator's element type, that, apparently, must be implicitly convertible to the `basic_regex::value_type`. For example, containers having range constructor satisfy a SequenceContainer requirements, where implicit converting to its elements is specified.

[2019-11-30 Issue Prioritization]

Priority to 3 after reflector discussion.

[2021-10-04; Jonathan adds a comment]

There isn't even a requirement that the arguments are iterators. And `assign(InputIterator, InputIterator, flag_type)` is not constrained to only accept iterators either, meaning you can call it with two integers and call the `basic_string(size_type, char_type)` constructor.

[2022-04-22; Jonathan adds a comment]

As well as requiring the reference type to be convertible to `charT`, we might want to consider constraining these with `forward_iterator` as per P2408.

Proposed resolution:

### 3342(i). Library wording uses "initializes `x` with `y`", which is underspecified

Section: 22.3.2 [pairs.pair], 22.4.4.2 [tuple.cnstr], 22.6.3.2 [variant.ctor], 32.5.8.7.2 [util.smartptr.atomic.shared], 20.6.3 [allocator.adaptor.cnstr], 28.5.6.6 [format.parse.ctx], 28.5.8.1 [format.arg], 23.6 [container.adaptors], 24.5 [predef.iterators], 25.5.4.2 [range.subrange.ctor], 25.6 [range.factories], 25.7 [range.adaptors], 26.10 [numeric.ops], 30.9 [time.hms], 28.6.11 [re.iter], 32.5.8 [atomics.types.generic], 32.5.9 [atomics.nonmembers], 32.3 [thread.stoptoken], 32.4 [thread.threads], 32.6 [thread.mutex], 32.8 [thread.sema], 32.9 [thread.coord], 32.10 [futures] Status: New Submitter: Richard Smith Opened: 2019-11-21 Last modified: 2019-12-08

Priority: 3

View other active issues in [pairs.pair].

View all other issues in [pairs.pair].

View all issues with New status.

Discussion:

The problem was discussed here:

> It seems to me that this is just one instance of a systemic problem in the library wording. This phrasing "initializes `x` with `y`" is common, but underspecified (and formally meaningless) — the library wording either needs to say what kind of initialization is performed, or specify an initializer (not an expression) with which to initialize. We should ask LWG to think about this; for each "initializes `x` with `y`" utterance, the reader should know what kind of initialization we mean.

> Looking at random through the library wording, the first case I found: 23.6.3.2 [queue.cons]/1:

> > Effects: Initializes `c` with `cont`.

> The meaning of this depends on whether this is direct- or copy-initialization. (It's obscure, but if `T` is not Cpp17CopyInsertable into the container, it could be the case that one form of initialization works and the other does not, or that they both work and do different things.)

> Another random sample: 26.10.7 [partial.sum]/2:

> > Effects: For a non-empty range, the function creates an accumulator `acc` whose type is `InputIterator`'s value type, initializes it with `*first`, and assigns the result to `*result`.

> Again the difference between direct- and copy-initialization is observable here.

> Perhaps the library should have blanket wording that when it says "initializes", it means by direct- or copy-initialization, and that it's unspecified which one you get (or something like that) — and someone should go through all the instances and check if any of them mean something else (I doubt this is the only case that does).

Suggestion: either

- add blanket wording defining what you mean when you say "initializes `x` with `y`" (e.g., it's unspecified whether copy-initialization or direct-initialization is performed) and make sure that that's what's intended for all uses, or

- stop using the "initializes `x` with `y`" formulation entirely, and specify the kind of initialization on each use, or

- for each such use, ensure that `y` is an initializer (that is, of the form "`= expr`" or "`(expr, expr, …)`" or "`= { … }`" or "`{ … }`", and not merely an expression)

[2019-12-08 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3344(i). `advance(i, most-negative)` and `prev(i, most-negative)`

Section: 24.4.3 [iterator.operations], 24.4.4.2 [range.iter.op.advance] Status: New Submitter: Casey Carter Opened: 2019-11-22 Last modified: 2019-12-07

Priority: 3

View other active issues in [iterator.operations].

View all other issues in [iterator.operations].

View all issues with New status.

Discussion:

`ranges::advance` (24.4.4.2 [range.iter.op.advance]) and `std::advance` (24.4.3 [iterator.operations]) can be called with a negative count `n` when the iterator argument `i` models `bidirectional_iterator` (respectively, meets the Cpp17BidirectionalIterator requirements). In this case, they are specified to "decrement `i` by `-n`". If `n` is the most-negative value of a signed integral type, the expression `-n` has undefined behavior. This UB is unfortunate given that typical implementations never actually form the expression `-n`. It's nonsensical to describe the effects of a function in terms of an expression with undefined behavior, so we should either define the behavior or exclude this case via precondition.

`ranges::prev()` and `std::prev` (24.4.3 [iterator.operations]) have a similar problem: `prev(i, n)` is equivalent to:

>     advance(i, -n);
>     return i;

which has undefined behavior when `n` is `numeric_limits<T>::min()` where `T` is `iter_difference_t<decltype(i)>` (for `ranges::prev`) or some signed integral type (for `std::prev`). There is an implicit precondition here thanks to "Effects: Equivalent to" since the equivalent code has a precondition that `n` is not a most-negative value, so this wording is not defective. We could, however, define behavior for `prev` regardless of the value of `n` by duplicating the specification of advance and inverting the "direction" of the operations. We should consider doing so.

[2019-12-07 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

This wording is relative to N4835.

> [Drafting note: I've chosen to provide wording for the conservative "define behavior for `advance` and leave `prev` as status quo" middle ground.

> The occurrences of "|" in the below are math-font vertical bars (indicating absolute value). I've changed both positive and negative cases for consistency of presentation. ]

1. Modify 24.4.3 [iterator.operations] as indicated:

   >     template<class InputIterator, class Distance>
   >       constexpr void advance(InputIterator& i, Distance n);

   > > -2- Expects: `n` is negative only for bidirectional iterators.

   > > -3- Effects: Increments `i` by `|n|` if `n` is non-negative, and decrements `i` by `-|n|` otherwise.

2. Modify 24.4.4.2 [range.iter.op.advance] as indicated:

   >     template<input_or_output_iterator I>
   >       constexpr void ranges::advance(I& i, iter_difference_t<I> n);

   > > -1- Expects: If `I` does not model `bidirectional_iterator`, `n` is not negative.

   > > -2- Effects:

   > > 1. (2.1) — If `I` models `random_access_iterator`, equivalent to `i += n`.

   > > 2. (2.2) — Otherwise, if `n` is non-negative, increments `i` by `|n|`.

   > > 3. (2.3) — Otherwise, decrements `i` by `-|n|`.

   > […]

   >     template<input_or_output_iterator I, sentinel_for<I> S>
   >       constexpr iter_difference_t<I> ranges::advance(I& i, iter_difference_t<I> n, S bound);

   > > -5- Expects: […]

   > > -6- Effects:

   > > 1. (6.1) — If `S` and `I` model `sized_sentinel_for<S, I>`:

   > >    1. (6.1.1) — If `|n| ≥ |bound - i|`, equivalent to `ranges::advance(i, bound)`.:

   > >    2. (6.1.2) — Otherwise, equivalent to `ranges::advance(i, n)`.

   > > 2. (6.2) — Otherwise,

   > >    1. (6.2.1) — if `n` is non-negative, while `bool(i != bound)` is `true`, increments `i` but at most `|n|` times.:

   > >    2. (6.2.2) — Otherwise, while `bool(i != bound)` is `true`, decrements `i` but at most `-|n|` times.

### 3353(i). `locale`'s copy assignment operator should return `locale&`

Section: 28.3.3.1 [locale] Status: Ready Submitter: Stephan T. Lavavej Opened: 2019-12-06 Last modified: 2026-06-10

Priority: 3

View all other issues in [locale].

View all issues with Ready status.

Discussion:

Curiously, `locale`'s copy assignment operator currently returns `const locale&`. As Casey Carter noted in microsoft/STL#268, this is:

- Weird!

- The only occurrence in the entire Standard Library.

- Preventing `locale` from satisfying `std::copyable`.

We aren't aware of any reason for this to be `const`. (I observe that this hasn't changed since N1804 on 2005-04-27 and probably goes back to C++98; I suspect that when this was originally specified, copy assignment operators were relatively new, and conventions for them weren't rigorously followed.)

[2019-12-21 Issue Prioritization]

Priority to 3 after reflector discussion based on the observation that we have implementation divergence.

[Brno 2026-06-10; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N4842.

1. Modify 28.3.3.1 [locale] as indicated:

   >     […]
   >     ~locale(); // not virtual
   >     const locale& operator=(const locale& other) noexcept;
   >     template<class Facet> locale combine(const locale& other) const;
   >     […]

2. Modify 28.3.3.1.3 [locale.cons] as indicated:

   >     const locale& operator=(const locale& other) noexcept;

   > > -14- Effects: Creates a copy of `other`, replacing the current value.

   > > -15- Returns: `*this`.

### 3357(i). [fund.ts.v3] `default_random_engine` is overspecified for per-thread engine

Section: 99 [fund.ts.v3::rand.util.randint] Status: Open Submitter: Zhihao Yuan Opened: 2019-12-10 Last modified: 2022-11-30

Priority: 3

View all issues with Open status.

Discussion:

Addresses: fund.ts.v3

Although "implementation may select this type on the basis of performance, size, quality, or any combination of such factors," but changing this typedef is an ABI-break for implementations. Specifying per-thread engine to use this typedef results in losses of performance, size, and/or quality.

Since this type is not involved in `randint` facilities' interface (other than its member typedef), the current specification should be relaxed.

[2020-01 Priority set to 3 and assigned to LEWG after review on the reflector.]

[2020-05-28; LEWG issue reviewing]

LEWG issue processing voted to reject 3357 as NAD. Status change to Open.

>     Reject LWG3357 as NAD

>     SF  F N A SA
>     1  10 4 2 1

[2022-10-19; Reflector poll]

Set status to "Tentatively NAD" based on LEWG recommendation and reflector poll.

[2022-11-30; LWG telecon]

Prefer to keep an open issue for the TS than to possibly forget to address it if this feature is proposed for the IS some day.

Proposed resolution:

This wording is relative to N4840.

1. Modify 11.1.1 [fund.ts.v3::rand.syn], header `<experimental/random>` synopsis, as indicated:

   >     #include <random>

   >     namespace std::experimental {
   >     inline namespace fundamentals_v3 {

   >       // 10.1.2.1, Function template randint
   >       template <class IntType>
   >       IntType randint(IntType a, IntType b);
   >       void reseed();
   >       void reseed(default_random_engine::result_typeuint_fast32_t value);

   >     } // inline namespace fundamentals_v3
   >     } // namespace std::experimental

2. Modify 99 [fund.ts.v3::rand.util.randint] as indicated:

   > -1- A separate per-thread engine of type `default_random_engine` (C++17 §29.6.5)unspecified type that meets the requirements of random number engine (C++17 [rand.req.eng]), initialized to an unpredictable state, shall be maintained for each thread. [Note: The implementation may choose the engine type on the basis of performance, size, quality, or any combination of such factors, so as to provide at least acceptable engine behavior for relatively casual, inexpert, and/or lightweight use. — end note]

   > […]

   >     void reseed();
   >     void reseed(default_random_engine::result_typeuint_fast32_t value);

   > > -7- Effects: Let `g` be the per-thread engine. The first form sets `g` to an unpredictable state. The second form invokes `g.seed(value)`.

   > > -8- Postconditions: Subsequent calls to `randint` do not depend on values produced by `g` before calling reseed. [Note: `reseed` also resets any instances of `uniform_int_distribution` used by `randint`. — end note]

### 3370(i). §[cstdint.syn]p2 and §[headers]p5 are not sufficiently clear

Section: 17.4.1 [cstdint.syn], 16.4.2.3 [headers] Status: New Submitter: Dawid Pilarski Opened: 2020-01-14 Last modified: 2020-01-25

Priority: 3

View all other issues in [cstdint.syn].

View all issues with New status.

Discussion:

This issue has been submitted, because the editorial change requests c++-draft-issue 3521 and c++-draft-pull request 3528 has been rejected as not being editorial changes:

Currently given wording of 17.4.1 [cstdint.syn]p2:

> The header defines all types and macros the same as the C standard library header `<stdint.h>`.

might be understood as intended: typedefs inside `stdint.h` and inside `cstdint` in namespace `std::` refer to the same types, but another interpretation could be, that it's understood as:

Definitions in both files are done the same way (`cstdint` provides typedefs not in namespace `std`, because it would be a different definition than one in `stdint.h`).

Also 16.4.2.3 [headers]p5 is non sufficiently clear:

> […] the contents of each header `cname` is the same as that of the corresponding header `name.h` […]

As it doesn't say what does "same content" mean. For example is an implementation allowed to do following:

>     // __impl.h
>     typedef int __my_int;
>     namespace std { typedef long __my_int; }

>     // cname header
>     #include "__impl.h"

>     namespace std {
>       typedef __my_int uint32_t;
>     }

>     // name.h header
>     #include "__impl.h"
>     typedef __my_int uint32_t;

?

In this case typedef from namespace `std` and from global namespace refer to different types?

Proposed change:

Apply wording, that will unambiguously make typedefs from namespace `std` refer to the same types as typedefs from global namespace for all headers `name.h` and their corresponding headers `cname`.

[2020-01-25 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3378(i). `tuple_size_v/tuple_element_t` should be available when `tuple_size/tuple_element` are

Section: 22.4.2 [tuple.syn], 22.4.7 [tuple.helper] Status: New Submitter: Casey Carter Opened: 2020-01-17 Last modified: 2021-11-04

Priority: 3

View all issues with New status.

Discussion:

22.4.7 [tuple.helper]/6 makes the `const`/`volatile`/`const volatile` partial specializations of `tuple_size` available when any of `<array>`, `<ranges>`, `<span>`, or `<utility>` is included. 22.4.7 [tuple.helper]/8 makes the `const`/`volatile`/`const volatile` partial specializations of `tuple_element` available when any of those same headers is included. This leads to a couple of problems:

1. For users of the Standard Library, it's not helpful to have these partial specializations of class templates available when the preferred interface — the variable template `tuple_size_v` and alias template `tuple_element_t` — are not.

2. For specifiers of the Standard Library, we must update two distinct yet identical lists of headers that make this same set of templates available when adding another header.

We could solve both of these problems by coalescing the two paragraphs into one and including the variable and alias template in the set of declarations made available by the pertinent (now single) list of headers.

[2020-02-08 Issue Prioritization]

Priority to 3 after reflector discussion. Tim Song said: It’s not clear that entities only mentioned in the synopsis are "defined in this subclause".

[2021-11-04; Jonathan Wakely adds note]

The `__cpp_lib_tuple_element_t` macro in 17.3.2 [version.syn] should be updated too.

Proposed resolution:

This wording is relative to N4842.

1. Modify 22.4.2 [tuple.syn], header `<tuple>` synopsis, as indicated:

   >     namespace std {
   >       […]

   >       // 22.4.7 [tuple.helper], tuple helper classes
   >       template<class T> struct tuple_size; // not defined
   >       template<class T> struct tuple_size<const T>;
   >       template<class T> struct tuple_size<volatile T>;
   >       template<class T> struct tuple_size<const volatile T>;

   >       template<class T>
   >         inline constexpr size_t tuple_size_v = tuple_size<T>::value;
   >       template<class... Types> struct tuple_size<tuple<Types...>>;

   >       template<size_t I, class T> struct tuple_element; // not defined
   >       template<size_t I, class T> struct tuple_element<I, const T>;
   >       template<size_t I, class T> struct tuple_element<I, volatile T>;
   >       template<size_t I, class T> struct tuple_element<I, const volatile T>;

   >       template<size_t I, class... Types>
   >         struct tuple_element<I, tuple<Types...>>;

   >       template<size_t I, class T>
   >         using tuple_element_t = typename tuple_element<I, T>::type;

   >       // 22.4.8 [tuple.elem], element access
   >       template<class... Types> struct tuple_size<tuple<Types...>>;

   >       template<size_t I, class... Types>
   >         struct tuple_element<I, tuple<Types...>>;

   >       template<size_t I, class... Types>
   >         constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>&) noexcept;
   >       template<size_t I, class... Types>
   >         constexpr tuple_element_t<I, tuple<Types...>>&& get(tuple<Types...>&&) noexcept;
   >       […]

   >       // 22.4.7 [tuple.helper], tuple helper classes
   >       template<class T>
   >         inline constexpr size_t tuple_size_v = tuple_size<T>::value;
   >     }

2. Modify 22.4.7 [tuple.helper] as indicated:

   > 20.5.6 Tuple helper classes [tuple.helper]

   > -?- In addition to being available via inclusion of the `<tuple>` header, the entities defined in this subclause [tuple.helper] are available when any of the headers `<array>` (23.3.2 [array.syn]), `<ranges>` (25.2 [ranges.syn]), `<span>` (23.7.2.1 [span.syn]), or `<utility>` (22.2.1 [utility.syn]) are included.

   >     template<class T> struct tuple_size;

   > > -1- Remarks: All specializations of `tuple_size` shall meet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic of `integral_constant<size_t, N>` for some `N`.

   >     template<class... Types>
   >       struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { };

   >     template<size_t I, class... Types>
   >       struct tuple_element<I, tuple<Types...>> {
   >         using type = TI;
   >       };

   > > -2- Requires: `I < sizeof...(Types)`. The program is ill-formed if `I` is out of bounds.

   > > -3- Type: `TI` is the type of the `Ith` element of `Types`, where indexing is zero-based.

   >     template<class T> struct tuple_size<const T>;
   >     template<class T> struct tuple_size<volatile T>;
   >     template<class T> struct tuple_size<const volatile T>;

   > > -4- Let `TS` denote `tuple_size<T>` of the cv-unqualified type `T`. If the expression `TS::value` is well-formed when treated as an unevaluated operand, then each of the three templates shall meet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic of

   > > >     integral_constant<size_t, TS::value>

   > > Otherwise, they shall have no member value.

   > > -5- Access checking is performed as if in a context unrelated to `TS` and `T`. Only the validity of the immediate context of the expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

   > > -6- In addition to being available via inclusion of the `<tuple>` header, the three templates are available when any of the headers `<array>` (23.3.2 [array.syn]), `<ranges>` (25.2 [ranges.syn]), `<span>` (23.7.2.1 [span.syn]), or `<utility>` (22.2.1 [utility.syn]) are included.

   >     template<size_t I, class T> struct tuple_element<I, const T>;
   >     template<size_t I, class T> struct tuple_element<I, volatile T>;
   >     template<size_t I, class T> struct tuple_element<I, const volatile T>;

   > > -7- Let `TE` denote `tuple_element_t<I, T>` of the cv-unqualified type `T`. Then each of the three templates shall meet the Cpp17TransformationTrait requirements (21.3.2 [meta.rqmts]) with a member typedef type that names the following type:

   > > 1. (7.1) — for the first specialization, `add_const_t<TE>`,

   > > 2. (7.2) — for the second specialization, `add_volatile_t<TE>`, and

   > > 3. (7.3) — for the third specialization, `add_cv_t<TE>`.

   > > -8- In addition to being available via inclusion of the `<tuple>` header, the three templates are available when any of the headers `<array>` (23.3.2 [array.syn]), `<ranges>` (25.2 [ranges.syn]), `<span>` (23.7.2.1 [span.syn]), or `<utility>` (22.2.1 [utility.syn]) are included.

3. Modify 22.4.8 [tuple.elem] as indicated:

   > [Drafting note: Since this issue performs colliding text changes with P1460R0, we perform similar wording changes as suggested on page 19 [tuple.helper] p2.]

   > 20.5.7 Element access [tuple.elem]

   >     template<class... Types>
   >       struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { };

   >     template<size_t I, class... Types>
   >       struct tuple_element<I, tuple<Types...>> {
   >         using type = TI;
   >       };

   > > -?- Mandates: `I < sizeof...(Types)`.

   > > -?- Type: `TI` is the type of the `Ith` element of `Types`, where indexing is zero-based.

   >     template<size_t I, class... Types>
   >       constexpr tuple_element_t<I, tuple<Types...>>&
   >         get(tuple<Types...>& t) noexcept;
   >     […]

   > > […]

### 3400(i). Does `is_nothrow_convertible` consider destruction of the destination type?

Section: 21.3.7 [meta.rel] Status: New Submitter: Jiang An Opened: 2020-02-10 Last modified: 2023-12-22

Priority: 3

View other active issues in [meta.rel].

View all other issues in [meta.rel].

View all issues with New status.

Discussion:

This issue was submitted after a previous editorial change request had been rejected by the project editors.

I find that all known implementations (at least msvcstl, libc++, libstdc++ and the sample in P0758R1) of `std::is_nothrow_convertible` may be not clear enough to indicate that whether destruction of the destination type is considered (or not).

For example, given a type `Weird` defined as

>     struct Weird
>     {
>       Weird(int) noexcept {}
>       ~Weird() noexcept(false) {}
>     };

Then `std::is_nothrow_convertible_v<int, Weird>` is `false` in every known implementation. However, it seems that the conversion itself is `noexcept`.

[2020-02-22, Daniel comments]

This seems to be quite related to the existing issue LWG 2116(i).

[2020-03-11 Issue Prioritization]

Priority to 3 after reflector discussion.

[2023-12-22; Daniel comments]

This issue should be resolved by LWG 4028(i).

Proposed resolution:

### 3401(i). Is "as if by" equivalent to "equivalent to"?

Section: 16.3.2.4 [structure.specifications] Status: New Submitter: Casey Carter Opened: 2020-02-14 Last modified: 2020-03-11

Priority: 3

View other active issues in [structure.specifications].

View all other issues in [structure.specifications].

View all issues with New status.

Discussion:

We have quite a few occurrences of the phrase "as if by" in Effects: elements in the library specification. Is the meaning of this phrase the same as "equivalent to"? If so, we should replace occurrences of "as if by" with "equivalent to" to make it clear that the magic "Effects: Equivalent to" wording in 16.3.2.4 [structure.specifications] para 4 is intended to apply.

[2020-03-11 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3402(i). Wording for `negative_binomial_distribution` is unclear as a consequence of LWG 2406 resolution

Section: 29.5.9.3.4 [rand.dist.bern.negbin] Status: SG6 Submitter: Ahti Leppänen Opened: 2020-02-17 Last modified: 2026-04-13

Priority: 3

View all other issues in [rand.dist.bern.negbin].

Discussion:

This issue has been created because a corresponding editorial change request had been rejected.

The resolution of LWG 2406(i) added a note to the definition of `negative_binomial_distribution`:

> [Note: This implies that P(i|k, p) is undefined when `p == 1`. — end note]

This issue argues that the note is invalid as are the premises on which LWG 2406 was based on. It's also argued that current normative standard text allowing `p == 1` is valid both conceptually and mathematically, and that it follows existing conventions in other software.

Problems with the added note:

- Why does `p == 1` imply that P(i|k, p) is undefined? The only questionable factor in the definition of P(i| k, p) seems to be that in case of `p == 1`, the factor `(1 - p)i` leads to `00` when `i == 0`. While it is true that there's no generally accepted convention what this means, `std::binomial_distribution` already uses the common convention `00 == 1` (e.g. with `p == 1 && t == i`, P(i|k, p) leads to `00`)

- Even if the term was undefined mathematically, does a non-normative note of mathematical term being undefined mean that the behaviour of the program is undefined (instead of e.g. resulting to NaN) even when no preconditions are violated?

- The note has lead to unclear situation of being able to construct a distribution object, but calling `operator()` might lead to undefined behaviour even though no preconditions are violated: for example the cppreference.com notes that

  > If `p == 1`, subsequent calls to the `operator()` overload that does not accept a `param_type` object will cause undefined behavior.

Invalidity of premises of LWG 2406:

- > For `p == 1`, this is "`* 1^k * 0^i`", so every integer `i >= 0` is produced with zero probability. (Let's avoid thinking about `0^0`.)

  - This is contradictory: first assuming that `0^i == 0` for all `i >= 0` (implying that `0^0 == 0`), but then comments not to think about `0^0`. The very essence of the issue is interpretation of `0^0` and given the definition of `binomial_distribution`, where `0^0 == 1`, the claim "so every integer `i >= 0` is produced with zero probability" can be considered faulty.

- > Wikipedia states that `p` must be within `(0, 1)`, exclusive on both sides.

  - I cannot find any mention of this in the Wikipedia's version as of 2014-06-02 (i.e. around the time when LWG 2406 was opened). Note that the Wikipedia's version is not the same as in C++ standard; in Wikipedia, `p` parameter is the same — i.e. the probability of success — but the integer parameter (`> 0`) is number of failures, while in C++ it is the number of successes. In the failure formulation `p == 1` is indeed invalid for essentially the same reason why `p == 0` is invalid for the C++ definition (i.e. leads to P(i|k, p) `== 0` for all `i`).

Validity of `p == 1`:

- > […] distribution of the number of failures in a sequence of trials with success probability `p` before `n` successes occur.

  (from Wolfram documentation). When `p == 1`, this means that trial always succeeds, so it's obvious that the probability to get 0 failures is 1, and the probability for `i > 0` failures is 0. This is exactly what the mathematical definition in 29.5.9.3.4 [rand.dist.bern.negbin] gives with convention `00 = 1` when `p == 1`.

- Software such as Mathematica, Matlab and R all accept `p == 1` for negative binomial distribution and they use the integer parameter as number of successes like the C++ standard.

What comes to the reasons why `p == 1` could have been considered invalid, it seems that major implementations — namely libstd++, libc++ and MSVC standard library — are using `std::gamma_distribution` in `std::negative_binomial_distribution` and passing `(1 - p)/p` as the second argument of `std::gamma_distribution`. Case `p == 1` is not checked leading to violation of precondition of `std::gamma_distribution`, which requires argument to be `> 0`.

For these reasons the note added by resolution of LWG 2406 seems invalid and could be considered for removal. However given the current status and history regarding handling of case `p == 1`, removing the note might not be the only option to consider.

[2020-03-11 Issue Prioritization]

Priority to 3 and hand over to SG6 after reflector discussion.

Proposed resolution:

### 3409(i). Too lax description of `atomic_ref<T>::required_alignment`

Section: 32.5.7.2 [atomics.ref.ops] Status: New Submitter: Andrey Semashev Opened: 2020-02-27 Last modified: 2020-09-06

Priority: 3

View all other issues in [atomics.ref.ops].

View all issues with New status.

Discussion:

N4849 32.5.7.2 [atomics.ref.ops]/1 describes `atomic_ref<T>::required_alignment` constant as follows:

> The alignment required for an object to be referenced by an atomic reference, which is at least `alignof(T)`.

This wording allows for an implementation to always define `required_alignment` to be equal to `alignof(T)` and implement atomic operations using locking, even if a lock-free implementation is possible at a higher alignment. For example, on x86-64, `atomic_ref<complex<double>>` could be lock-free only when the referred object is aligned to 16 bytes, but the above definition allows an implementation to define `required_alignment` to 8 and use locking.

The note in 32.5.7.2 [atomics.ref.ops]/2 does mention that lock-free operations may require higher alignment, but it does not provide guidance to the implementations so that `required_alignment` reflects alignment required for lock-free operations, if possible, and not just minimum alignment required for any kind of implementation.

The suggested resolution is to change the wording so that it is clear that `required_alignment` indicates the alignment required for lock-free implementation, if one is possible, or `alignof(T)` otherwise.

Further, the note in 32.5.7.2 [atomics.ref.ops]/2 contains this sentence:

> Further, whether operations on an `atomic_ref` are lock-free could depend on the alignment of the referenced object.

This sentence is misleading, because according to `is_lock_free()` definition in 32.5.7.2 [atomics.ref.ops]/4, the lock-free property is not allowed to depend on the alignment of a particular referenced object (`is_lock_free()` must return `true` or `false` if operations on all objects of the given type `T` are lock-free or not). In other words, `atomic_ref` can only refer to an object aligned at least to `required_alignment` and its lock-free capability cannot depend on the actual runtime alignment of the object.

To avoid the confusion, I propose to remove the sentence. The rest of the note can stay intact. However, this second edit is less important than the first one and can be omitted in case of disagreement.

[2020-04-04 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

This wording is relative to N4849.

1. Modify 32.5.7.2 [atomics.ref.ops] as indicated:

   >     static constexpr size_t required_alignment;

   > > -1- Let `A` be tThe alignment required for an object to be referenced by an atomic reference, which is at least `alignof(T)`so that `is_always_lock_free` is `true`. If there is no such alignment or `A` is less than `alignof(T)`, `required_alignment` equals `alignof(T)`. Otherwise, `required_alignment` equals `A`.

   > > -2- [Note: Hardware could require an object referenced by an `atomic_ref` to have stricter alignment (6.8.3 [basic.align]) than other objects of type `T`. Further, whether operations on an `atomic_ref` are lock-free could depend on the alignment of the referenced object. For example, lock-free operations on `std::complex<double>` could be supported only if aligned to `2*alignof(double)`. — end note]

### 3416(i). The Throws: specification of `std::any` does not mention allocation

Section: 22.7.4 [any.class] Status: New Submitter: Thomas Köppe Opened: 2020-03-04 Last modified: 2020-04-04

Priority: 3

View all other issues in [any.class].

View all issues with New status.

Discussion:

Several of the function specifications in 22.7.4 [any.class] have Throws: elements, but those only mention "exceptions thrown by a constructor". It seems necessary for `std::any` to perform dynamic allocation in general, and so in general there should be a possibility of an exception raised by such dynamic allocation. (This may come from a user-provided `T::operator new`, as far as I can tell.)

We should revise the specifications to add relevant sources of exceptions.

The functions that should probably mention allocations are:

- `any(const any& other)`

- `template<class T> any(T&& value)`

- both `any(in_place_t<T>, …)` overloads

- `any& operator=(const any& rhs)`

- `template<class T> any& operator=(T&& rhs)`

- all `emplace` overloads

Proposed wording: None yet. Maybe insert something like "thrown by unspecified, internal bookkeeping logic" into each Throws: element, but perhaps something more specific is necessary.

[2020-04-04 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

### 3418(i). Deprecated free functions in `<atomic>`

Section: 32.5.9 [atomics.nonmembers] Status: New Submitter: Alisdair Meredith Opened: 2020-03-19 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

Paper P1831R1 deprecated the `volatile`-qualified member functions of `std::atomic` unless `is_always_lock_free` is `true`. 32.5.9 [atomics.nonmembers] maps free functions calls, declared in the `<atomic>` header, to those member functions, but does not deprecate them under the same circumstances.

I have confirmed with the paper author that the intended design was to deprecate these too, but currently we have no wording.

[2020-03-29; Daniel provides wording]

The suggested wording changes for 32.5.9 [atomics.nonmembers] attempts to make clear that any of the specification elements of the member function (including but not restricted to Constraints: elements) are also imposed on the corresponding non-member function template. According to 16.3.2.4 [structure.specifications], the wording "the semantics of the code sequence are determined by the Constraints,[…], and Error conditions specified for the function invocations contained in the code sequence." should realize the wanted effect. The advantage of this more general wording form is that we don't need to to worry in case that in the future Constraints: elements of the member functions are modified.

[2020-03-30; Tim improves wording]

[2020-04-25 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

This wording is relative to N4861.

1. Modify 32.5.9 [atomics.nonmembers] as indicated:

   > -1- A non-member function template whose name matches the pattern `atomic_f` or the pattern `atomic_f_explicit` invokes the member function `f`, with the value of the first parameter as the object expression and the values of the remaining parameters (if any) as the arguments of the member function call, in order. An argument for a parameter of type `atomic<T>::value_type*` is dereferenced when passed to the member function call. If no such member function exists, the program is ill-formed. Otherwise, a call to such a function template has effects equivalent to (16.3.2.4 [structure.specifications]) the effective code sequence containing the `f` invocation specified in this subclause.

2. Modify D.24.2 [depr.atomics.volatile], annex D, as indicated:

   > If an atomic specialization has one of the following overloads, then that overload participates in overload resolution even if `atomic<T>::is_always_lock_free` is `false`:

   > >     void store(T desired, memory_order order = memory_order::seq_cst) volatile noexcept;
   > >     […]
   > >     T* fetch_key(ptrdiff_t operand, memory_order order = memory_order::seq_cst) volatile noexcept;

   > In addition, the following non-member function templates participate in overload resolution even if `atomic<T>::is_always_lock_free` is `false`:

   > >     template<class T>
   > >       void atomic_store(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
   > >     template<class T>
   > >       T atomic_load(const volatile atomic<T>*) noexcept;
   > >     template<class T>
   > >       T atomic_load_explicit(const volatile atomic<T>*, memory_order) noexcept;
   > >     template<class T>
   > >       T atomic_exchange(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
   > >     template<class T>
   > >     T atomic_exchange_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
   > >       memory_order) noexcept;
   > >     template<class T>
   > >       bool atomic_compare_exchange_weak(volatile atomic<T>*,
   > >                                         typename atomic<T>::value_type*,
   > >                                         typename atomic<T>::value_type) noexcept;
   > >     template<class T>
   > >       bool atomic_compare_exchange_strong(volatile atomic<T>*,
   > >                                           typename atomic<T>::value_type*,
   > >                                           typename atomic<T>::value_type) noexcept;
   > >     template<class T>
   > >       bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*,
   > >                                                  typename atomic<T>::value_type*,
   > >                                                  typename atomic<T>::value_type,
   > >                                                  memory_order, memory_order) noexcept;
   > >     template<class T>
   > >       bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*,
   > >                                                    typename atomic<T>::value_type*,
   > >                                                    typename atomic<T>::value_type,
   > >                                                    memory_order, memory_order) noexcept;
   > >     template<class T>
   > >       T atomic_fetch_key(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept;
   > >     template<class T>
   > >       T atomic_fetch_key_explicit(volatile atomic<T>*, typename atomic<T>::difference_type,
   > >                                   memory_order) noexcept;
   > >     template<class T>
   > >       T atomic_fetch_key(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
   > >     template<class T>
   > >       T atomic_fetch_key_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
   > >                                   memory_order) noexcept;
   > >     template<class T>
   > >       void atomic_wait(const volatile atomic<T>*, typename atomic<T>::value_type);
   > >     template<class T>
   > >       void atomic_wait_explicit(const volatile atomic<T>*, typename atomic<T>::value_type,
   > >                                 memory_order);
   > >     template<class T>
   > >       void atomic_notify_one(volatile atomic<T>*);
   > >     template<class T>
   > >       void atomic_notify_all(volatile atomic<T>*);

### 3423(i). `std::any_cast` should never return a cv-qualified type

Section: 22.7.5 [any.nonmembers] Status: New Submitter: Casey Carter Opened: 2020-04-02 Last modified: 2020-09-06

Priority: 3

View all other issues in [any.nonmembers].

View all issues with New status.

Discussion:

The return type of the non-pointer overloads of `std::any_cast<T>` is `T`. This is silly when `T` is cv-qualified (and, since merging P1152 "Deprecating volatile" into the working draft, deprecated when `T` is `volatile`-qualified). We should strip cv-qualifiers to determine the type returned.

[2020-04-18 Issue Prioritization]

Priority to 3 after reflector discussion.

Proposed resolution:

This wording is relative to N4861.

1. Modify 22.7.2 [any.synop] as indicated:

   >     […]
   >     template<class T>
   >       remove_cv_t<T> any_cast(const any& operand);
   >     template<class T>
   >       remove_cv_t<T> any_cast(any& operand);
   >     template<class T>
   >       remove_cv_t<T> any_cast(any&& operand);
   >     […]

2. Modify 22.7.5 [any.nonmembers] as indicated:

   >     template<class T>
   >       remove_cv_t<T> any_cast(const any& operand);
   >     template<class T>
   >       remove_cv_t<T> any_cast(any& operand);
   >     template<class T>
   >       remove_cv_t<T> any_cast(any&& operand);

   > > -4- Let `R` be the type `remove_cv_t<T>`, and `U` be the type `remove_cvref_t<T>`.

   > > -5- Mandates: For the first overload, `is_constructible_v<TR, const U&>` is `true`. For the second overload, `is_constructible_v<TR, U&>` is `true`. For the third overload, `is_constructible_v<TR, U>` is `true`.

   > > -6- Returns: For the first and second overload, `static_cast<TR>(*any_cast<U>(&operand))`. For the third overload, `static_cast<TR>(std::move(*any_cast<U>(&operand)))`.

   > > -7- Throws: `bad_any_cast` if `operand.type() != typeid(remove_reference_t<T>)`.

### 3424(i). `optional::value_or` should never return a cv-qualified type

Section: 22.5.3.7 [optional.observe] Status: New Submitter: Casey Carter Opened: 2020-04-02 Last modified: 2026-06-09

Priority: 3

View other active issues in [optional.observe].

View all other issues in [optional.observe].

View all issues with New status.

Discussion:

The `optional<T>::value_or` overloads are specified to return `T`. This seems silly when `T` is `const` or `volatile` qualified — return types should never be cv-qualified. (In the `volatile` case, it is even deprecated since merging P1152R4 "Deprecating volatile" into the working draft.) We should strip cv-qualifiers from these return types.

[2020-04-18 Issue Prioritization]

Priority to 3 after reflector discussion.

Previous resolution [SUPERSEDED]

This wording is relative to N4861.

1. Modify 22.5.3 [optional.optional] as indicated:

   >     […]
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) const&;
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

2. Modify 22.5.3.7 [optional.observe] as indicated:

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) const&;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -17- Mandates: `is_copy_constructible_v<T>is_convertible_v<const T&, R> && is_convertible_v<U&&, T>` is `true`.

   > > -18- Effects: Equivalent to:

   > > >     return bool(*this) ? **this : static_cast<TR>(std::forward<U>(v));

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -19- Mandates: `is_move_constructible_v<T>is_convertible_v<T, R> && is_convertible_v<U&&, T>` is `true`.

   > > -20- Effects: Equivalent to:

   > > >     return bool(*this) ? std::move(**this) : static_cast<TR>(std::forward<U>(v));

[2023-02-09 Casey improves wording and expands to cover `expected::value_or`]

Since `expected` was modeled on `optional`, it has the same issue.

[2025-11-19; This would be resolved by 4406(i).]

Proposed resolution:

This wording is relative to N4928.

1. Modify 22.5.3.1 [optional.optional.general] as indicated:

   >     […]
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) const&;
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

2. Modify 22.5.3.7 [optional.observe] as indicated:

   > [Drafting note: The two removals of the `&&` in `is_convertible_v<U&&, T>` below is a simplification to restore consistency with the wording for `expected::value_or`.]

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -15- Mandates: `is_copy_constructible_v<T> is_convertible_v<const T&, R> && is_convertible_v<U&&, TR>` is `true`.

   > > -16- Effects: Equivalent to:

   > > >     return bool(*this) ? **this : static_cast<TR>(std::forward<U>(v));

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -17- Mandates: `is_move_constructible_v<T> is_convertible_v<T, R> && is_convertible_v<U&&, TR>` is `true`.

   > > -18- Effects: Equivalent to:

   > > >     return bool(*this) ? std::move(**this) : static_cast<TR>(std::forward<U>(v));

3. Modify 22.8.6.1 [expected.object.general] as indicated:

   >     […]
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) const &;
   >     template<class U> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

4. Modify 22.8.6.6 [expected.object.obs] as indicated:

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -16- Mandates: `is_copy_constructible_v<T> is_convertible_v<const T&, R> && is_convertible_v<U, TR>` is `true`.

   > > -17- Returns: `has_value() ? **this : static_cast<TR>(std::forward<U>(v))`.

   >     template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `R` be `remove_cv_t<T>`.

   > > -18- Mandates: `is_move_constructible_v<T> is_convertible_v<T, R> && is_convertible_v<U, TR>` is `true`.

   > > -19- Returns: `has_value() ? std::move(**this) : static_cast<TR>(std::forward<U>(v))`.

### 3429(i). "models" should subsume like "satisfies"

Section: 16.4.5.11 [res.on.requirements] Status: New Submitter: Tim Song Opened: 2020-04-07 Last modified: 2020-09-06

Priority: 3

View other active issues in [res.on.requirements].

View all other issues in [res.on.requirements].

View all issues with New status.

Discussion:

It has been pointed out both on the LWG reflector and as editorial issue 3912 that the definition of "models" added in P2101R0 is not a model of clarity when a concept is defined in terms of other concepts.

[2020-04-25 Issue Prioritization]

Priority to 3 after reflector discussion. There was a temptation to resolve this as P0, but concerns were expressed that the "satisfied as part of the satisfaction" part was a bit confusing.

Proposed resolution:

This wording is relative to N4861.

1. Modify 16.4.5.11 [res.on.requirements] as indicated:

   > -1- A sequence `Args` of template arguments is said to model a concept `C` if:

   > 1. (1.1) — `Args` satisfies `C` (13.5.3 [temp.constr.decl]);

   > 2. (1.2) — and `Args` meets all semantic requirements (if any) given in the specification of `C`; and

   > 3. (1.3) — every concept found to be satisfied as part of the satisfaction determination above is also modeled.

### 3438(i). §[container.node.overview] missing `multiset`/`map` cases

Section: 23.2.5.1 [container.node.overview] Status: New Submitter: Jens Maurer Opened: 2020-04-30 Last modified: 2020-05-09

Priority: 3

View all other issues in [container.node.overview].

View all issues with New status.

Discussion:

This issue resulted out of this editorial change request.

In 23.2.5.1 [container.node.overview], there is Table 79 [tab:container.node.compat] which indicates which containers have compatible nodes.

It appears that rows along

>     multimap<K, T, C1, A>     multimap<K, T, C2, A>

(i.e. multimaps with differing `Compare` functions) are missing from the table.

(Same for `multiset` and `unordered_multiset/map`.)

However, the introductory sentences in 23.2.5.1 [container.node.overview] do not relate "compatible nodes" with Table 79 and do not clearly state transitivity:

> A node handle is an object that accepts ownership of a single element from an associative container (23.2.7 [associative.reqmts]) or an unordered associative container (23.2.8 [unord.req]). It may be used to transfer that ownership to another container with compatible nodes. Containers with compatible nodes have the same node handle type. Elements may be transferred in either direction between container types in the same row of Table 79 [tab:container.node.compat].

[2020-05-09; Reflector prioritization]

Set priority to 3 after reflector discussions.

Proposed resolution:

### 3439(i). "`Distance`" template parameter is underspecified

Section: 24.4.3 [iterator.operations], 26.7.12 [alg.random.sample] Status: New Submitter: Jens Maurer Opened: 2020-05-01 Last modified: 2025-10-10

Priority: 3

View other active issues in [iterator.operations].

View all other issues in [iterator.operations].

View all issues with New status.

Discussion:

The library specification conveys pre-concept requirements on template parameters by naming, e.g. "`InputIterator`".

There is no general specification for template parameters named "`Distance`", as used by `std::advance` (see 24.4.3 [iterator.operations]). When addressing this omission, the local restriction in 26.7.12 [alg.random.sample] could possibly be removed.

See the related issue 3213(i) for the `Size` template parameter.

[2025-10-10; Jonathan comments]

The SGI STL has the requirement that "`Distance` is an integral type that is convertible to `InputIterator`'s distance type." But the SGI STL also required that `Size` arguments to `fill_n` etc. have integral types, and the standard only requires them to be convertible to integral types. Presumably that relaxation was intentional.

[2020-05-09; Reflector prioritization]

Set priority to 3 after reflector discussions.

Proposed resolution:

### 3444(i). [networking.ts] `net::basic_socket_streambuf::connect(Args&&...)` effects are wrong

Section: 19.1.2 [networking.ts::socket.streambuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-05-14 Last modified: 2020-07-17

Priority: 2

View all issues with New status.

Discussion:

Addresses: networking.ts

The effects in 19.1.2 [networking.ts::socket.streambuf.members] p3 say that the function loops through every endpoint in the sequence, attempting to establish a connection. It needs to say that as soon as a connection is successfully established it returns. Otherwise even if a connection is made, it closes the socket and tries the next endpoint in the sequence. That means it will always be left in whatever state resulted from trying the last endpoint in the sequence (or from timing out if the expiry time was reached before iterating through all endpoints).

[2020-07-17; Priority set to 2 in telecon]

Jonathan to provide wording.

Proposed resolution:

### 3445(i). [networking.ts] `net::basic_socket_istream::connect` should be constrained

Section: 19.2.1 [networking.ts::socket.iostream.cons], 19.2.2 [networking.ts::socket.iostream.members] Status: LEWG Submitter: Jonathan Wakely Opened: 2020-05-14 Last modified: 2025-10-16

Priority: 3

View all other issues in [networking.ts::socket.iostream.cons].

View all issues with LEWG status.

Discussion:

Addresses: networking.ts

`basic_socket_streambuf<P, C, W>::connect(Args&&...)` is constrained to only exist when `P` meets the `InternetProtocol` requirements, but `basic_socket_iostream<P, C, W>::connect(Args&&...)` is not constrained. Since it just passes those arguments straight to the `streambuf`, the outer `connect(Args&&...)` should be constrained too.

In addition to that, the `basic_socket_iostream(Args&&...)` constructor should be constrained, so that `is_constructible` gives the right answer.

[2020-07-17; Priority set to 3 in telecon]

[2025-10-16 Status changed: LEWG → SG4.]

Discussed by LEWG in Wrocław, recommended to send to SG4.

Proposed resolution:

This wording is relative to N4771.

1. Modify 19.2.1 [networking.ts::socket.iostream.cons] as indicated:

   > [Drafting note: As a drive-by fix, a missing `std::` qualification in front of `forward` has been added]

   >     template<class... Args>
   >       explicit basic_socket_iostream(Args&&... args);

   > > -4- Effects: Initializes the base class as `basic_iostream<char>(&sb_))`, value-initializes `sb_`, and performs `setf(std::ios_base::unitbuf)`. Then calls `rdbuf()->connect(std::forward<Args>(args)...)`. If that function returns a null pointer, calls `setstate(failbit)`.

   > > -?- Remarks: This function shall not participate in overload resolution unless the expression `rdbuf()->connect(std::forward<Args>(args)...)` is well-formed.

2. Modify 19.2.2 [networking.ts::socket.iostream.members] as indicated:

   > [Drafting note: As a drive-by fix, a missing `std::` qualification in front of `forward` has been added]

   >     template<class... Args>
   >       void connect(Args&&... args);

   > > -1- Effects: Calls `rdbuf()->connect(std::forward<Args>(args)...)`. If that function returns a null pointer, calls `setstate(failbit)` (which may throw `ios_base::failure`).

   > > -?- Remarks: This function shall not participate in overload resolution unless the expression `rdbuf()->connect(std::forward<Args>(args)...)` is well-formed.

### 3451(i). Inconsistently explicit deduction guides

Section: 27.4.3 [basic.string], 25.7.14.2 [range.join.view] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-06-11 Last modified: 2020-07-17

Priority: 3

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with New status.

Discussion:

The library inconsistently marks deduction guides as `explicit`. `join_view` and `basic_string` account for the only two occurrences of unconditionally explicit deduction guides. All other deduction guides have no `explicit`-specifier. Following is a list of unconditionally explicit constructors with their deduction guides.

>     template<class Y>
>       explicit shared_ptr(const weak_ptr<Y>& r);

>     template<class T>
>       shared_ptr(weak_ptr<T>) -> shared_ptr<T>;

>     template<class T>
>       constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());

>     template<class charT,
>              class traits,
>              class Allocator = allocator<charT>>
>       explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
>         -> basic_string<charT, traits, Allocator>;

>     explicit queue(const Container&);
>     explicit queue(Container&&);

>     template<class Container>
>       queue(Container) -> queue<typename Container::value_type, Container>;

>     explicit stack(const Container&);
>     explicit stack(Container&&);

>     template<class Container>
>       stack(Container) -> stack<typename Container::value_type, Container>;

>     constexpr explicit join_view(V base);

>     template<class R>
>       explicit join_view(R&&) -> join_view<views::all_t<R>>;

>     constexpr explicit common_view(V r);

>     template<class R>
>       common_view(R&&) -> common_view<views::all_t<R>>;

>     constexpr explicit reverse_view(V r);

>     template<class R>
>       reverse_view(R&&) -> reverse_view<views::all_t<R>>;

>     explicit zoned_time(TimeZonePtr z);
>     explicit zoned_time(string_view name);

>     template<class TimeZonePtrOrName>
>       zoned_time(TimeZonePtrOrName&&)
>         -> zoned_time<seconds, time-zone-representation<TimeZonePtrOrName>>;

>     template<class C>
>     explicit stop_callback(const stop_token& st, C&& cb)
>         noexcept(is_nothrow_constructible_v<Callback, C>);
>     template<class C>
>     explicit stop_callback(stop_token&& st, C&& cb)
>         noexcept(is_nothrow_constructible_v<Callback, C>);

>     template<class Callback>
>       stop_callback(stop_token, Callback) -> stop_callback<Callback>;

[2020-07-17; Priority set to 3 in telecon]

Proposed resolution:

This wording is relative to N4861.

1. Modify 27.4.3 [basic.string], class template `basic_string` synopsis, as indicated:

   >     […]
   >     template<class charT,
   >              class traits,
   >              class Allocator = allocator<charT>>
   >       explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
   >         -> basic_string<charT, traits, Allocator>;
   >     […]

2. Modify 27.4.3.3 [string.cons] as indicated:

   >     […]
   >     template<class charT,
   >              class traits,
   >              class Allocator = allocator<charT>>
   >       explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
   >         -> basic_string<charT, traits, Allocator>;
   >     […]

   > > -22- Constraints: `Allocator` is a type that qualifies as an allocator (23.2.2 [container.requirements.general]).

3. Modify 25.7.14.2 [range.join.view], class template `join_view` synopsis, as indicated:

   >     […]
   >     template<class R>
   >       explicit join_view(R&&) -> join_view<views::all_t<R>>;
   >     […]

### 3456(i). Pattern used by `std::from_chars` is underspecified

Section: 28.2.3 [charconv.from.chars] Status: New Submitter: Jonathan Wakely Opened: 2020-06-23 Last modified: 2026-04-07

Priority: 3

View other active issues in [charconv.from.chars].

View all other issues in [charconv.from.chars].

View all issues with New status.

Discussion:

The intention of 28.2.3 [charconv.from.chars] p7 is that the `fmt` argument modifies the expected pattern, so that only a specific subset of valid `strtod` patterns are recognized for each format. This is not clear from the wording.

When `fmt == chars_format::fixed` no exponent is to be used, so any trailing characters that match the form of a `strtod` exponent are ignored. For example, `"1.23e4"` should produce the result `1.23` for the fixed format. The current wording says "the optional exponent part shall not appear" which can be interpreted to mean that `"1.23e4"` violates a precondition and so has undefined behaviour!

When `fmt != chars_format::hex` only decimal numbers should be recognized. This means that for any format except scientific, `"0x123"` produces `0.0` (it's invalid when `fmt == chars_format::scientific` because there's no exponent). The current wording only says that when `hex` is used the string has an assumed `"0x"` prefix, so is interpreted as a hexadecimal float, it doesn't say that when `fmt != hex` that the string is not interpreted as a hexadecimal float.

Two alternative resolutions are provided, one is a minimal fix and the other attempts to make it clearer by not referring to a modified version of the C rules.

[2020-07-14; Jonathan fixes the `strtod` call in Option B]

[2020-07-17; Priority set to 3 in telecon]

[2026-04-07; P4168R0 addresses this]

Proposed resolution:

This wording is relative to N4861.

Option A:

1. Modify 28.2.3 [charconv.from.chars] as indicated:

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Preconditions: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for `strtod`, except that

   > > 1. (7.1) — the sign `'+'` may only appear in the exponent part;

   > > 2. (7.2) — if `fmt` has `chars_format::scientific` set but not `chars_format::fixed`, the otherwise optional exponent part shall appearexponent part is not optional;

   > > 3. (7.3) — if `fmt` has `chars_format::fixed` set but not `chars_format::scientific`, the optional exponent part shall not appear; andthere is no exponent part;

   > > 4. (?.?) — if `fmt` is not `chars_format::hex`, only decimal digits and an optional `'.'` appear before the exponent part (if any); and

   > > 5. (7.4) — if `fmt` is `chars_format::hex`, the prefix `"0x"` or `"0X"` is assumed. [Example: The string `0x123` is parsed to have the value `0` with remaining characters `x123`. — end example]

   > > In any case, the resulting `value` is one of at most two floating-point values closest to the value of the string matching the pattern.

Option B:

1. Modify 28.2.3 [charconv.from.chars] as indicated:

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Preconditions: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for `strtod`, except thatThe pattern is an optional `'-'` sign followed by one of:

   > > 1. (7.1) — the sign `'+'` may only appear in the exponent part `INF` or `INFINITY`, ignoring case;

   > > 2. (7.2) — if `fmt` has `chars_format::scientific` set but not `chars_format::fixed`, the otherwise optional exponent part shall appearif `numeric_limits<T>::has_quiet_NaN` is `true`, `NAN` or `NAN(`n-char-sequenceopt`)`, ignoring case in the `NAN` part, where:

   > >    >     n-char-sequence:
   > >    >            digit
   > >    >            nondigit
   > >    >            n-char-sequence digit
   > >    >            n-char-sequence nondigit

   > >    ;

   > > 3. (7.3) — if `fmt` has `chars_format::fixed` set but not `chars_format::scientific`, the optional exponent part shall not appear; andif `fmt` is equal to `chars_format::scientific`, a sequence of characters matching chars-format-dec exponent-part, where:

   > >    >     chars-format-dec:
   > >    >              fractional-constant
   > >    >              digit-sequence

   > >    ;

   > > 4. (7.4) — if `fmt` is `chars_format::hex`, the prefix `"0x"` or `"0X"` is assumed. [Example: The string `0x123` is parsed to have the value `0` with remaining characters `x123`. — end example]if `fmt` is equal to `chars_format::fixed`, a sequence of characters matching chars-format-dec;

   > > 5. (?.?) — if `fmt` is equal to `chars_format::general`, a sequence of characters matching chars-format-dec exponent-partopt; or

   > > 6. (?.?) — if `fmt` is equal to `chars_format::hex`, a sequence of characters matching chars-format-hex binary-exponent-partopt, where:

   > >    >     chars-format-hex:
   > >    >              hexadecimal-fractional-constant
   > >    >              hexadecimal-digit-sequence

   > >    [Note: The pattern is derived from the subject sequence in the `"C"` locale for `strtod`, with the value of `fmt` limiting which forms of the subject sequence are recognized, and with no `0x` or `0X` prefix recognized. — end note]

   > > For a character sequence `INF`, `INFINITY`, `NAN`, or `NAN(`n-char-sequenceopt`)` the resulting value is obtained as if by evaluating `strtod(string(first, last).c_str(), nullptr)` in the `"C"` locale. In all other casesIn any case, the resulting `value` is one of at most two floating-point values closest to the value of the string matching the pattern.

### 3457(i). `*this` is not invalidated

Section: 27.3.3 [string.view.template] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-06-26 Last modified: 2020-09-06

Priority: 3

View all other issues in [string.view.template].

View all issues with New status.

Discussion:

27.3.3 [string.view.template] states:

> For a `basic_string_view str`, any operation that invalidates a pointer in the range `[str.data(), str.data() + str.size())` invalidates pointers, iterators, and references returned from `str`'s member functions.

The assignment operators return a reference to `*this`, but `*this` is not invalidated.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4861.

> 1. Modify 27.3.3 [string.view.template] as indicated:

>    > -2- For a `basic_string_view str`, any operation that invalidates a pointer in the range `[str.data(), str.data() + str.size())` invalidates pointers, iterators, and references to the elements in that range and its past-the-end iterator returned from `str`'s member functions.

[2020-06-29; Casey comments and provides alternative proposed wording]

I think we should additionally strike "returned from `str`'s member functions" from the end of the sentence. Provenance shouldn't affect invalidation; `ranges::next(some_string_view.begin(), 42)`, for example, returns an iterator that denotes an element of `some_string_view`, but is not a member function of `basic_string_view`.

[2020-07-05; Reflector prioritization]

Set priority to 3 after reflector discussions.

Proposed resolution:

This wording is relative to N4861.

1. Modify 27.3.3 [string.view.template] as indicated:

   > -2- For a `basic_string_view str`, any operation that invalidates a pointer in the range `[str.data(), str.data() + str.size())` invalidates pointers, iterators, and references to the elements in that range and its past-the-end iteratorreturned from `str`'s member functions.

### 3459(i). Why doesn't `std::convertible_to` have semantic requirement when `To` is reference-to-function type?

Section: 18.4.4 [concept.convertible] Status: New Submitter: S. B. Tam Opened: 2020-06-30 Last modified: 2020-07-12

Priority: 3

View other active issues in [concept.convertible].

View all other issues in [concept.convertible].

View all issues with New status.

Discussion:

18.4.4 [concept.convertible] p2:

> Types `From` and `To` model `convertible_to<From, To>` only if:

> 1. (2.1) — `To` is not an object or reference-to-object type, or `static_cast<To>(f())` is equal to `test(f)`.

> 2. […]

This requires the implicit and explicit conversions to produce equal results.

However, it seems that when `To` is a reference-to-function type, this restriction does not apply. This makes it possible to create a class that models `convertible_to`, but produces different results depending on the kind of conversion:

>     #include <concepts>

>     int foo() { return 0; }
>     int bar() { return 42; }

>     using FT = int();
>     struct A
>     {
>       operator FT&() const { return foo; }
>       explicit operator FT&() { return bar; }
>     };

>     static_assert(std::convertible_to<A, FT&>);

>     A a;
>     FT& x = a;                    // x == foo
>     auto y = static_cast<FT&>(a); // y == bar

[2020-07-12; Reflector prioritization]

Set priority to 3 after reflector discussions.

Proposed resolution:

### 3463(i). Incorrect requirements for `transform_inclusive_scan` without initial value

Section: 26.10.11 [transform.inclusive.scan] Status: New Submitter: Agustín K-ballo Bergé Opened: 2020-07-07 Last modified: 2020-09-06

Priority: 3

View all issues with New status.

Discussion:

The requirements for the overloads of `std::transform_inclusive_scan` without an initial value incorrectly assume that the internal accumulator uses the iterator's value type, as it does for `std::inclusive_scan`, rather than the transformed type of the iterator's value type, as it was intended.

According to the standard, the following program is ill-formed as it requires `std::string` to be convertible to `int`:

>     auto vs = {0, 1, 2};
>     std::transform_inclusive_scan(
>        vs.begin(), vs.end(),
>        std::ostream_iterator<std::string>(std::cout, ";"),
>        [](std::string x, std::string y) { return x + y; },
>        [](int x) { return std::to_string(x); });

libstdc++ and Microsoft's STL accept the snippet, producing `0;01;012;` as expected, libc++ strictly conforms to the standard and rejects it.

These constrains were introduced by P0574R1.

[2020-07-17; Priority set to 3 in telecon]

Proposed resolution:

This wording is relative to N4861.

> [Drafting note: Current implementations that accept the code, do some form of `auto acc = unary_op(*first);`, therefore the following proposed wording uses `decay_t` instead of e.g. `remove_cvref_t`.]

1. Modify 26.10.11 [transform.inclusive.scan] as indicated:

   >     template<class InputIterator, class OutputIterator,
   >              class BinaryOperation, class UnaryOperation>
   >       constexpr OutputIterator
   >         transform_inclusive_scan(InputIterator first, InputIterator last,
   >                                  OutputIterator result,
   >                                  BinaryOperation binary_op, UnaryOperation unary_op);
   >     template<class ExecutionPolicy,
   >              class ForwardIterator1, class ForwardIterator2,
   >              class BinaryOperation, class UnaryOperation>
   >       ForwardIterator2
   >         transform_inclusive_scan(ExecutionPolicy&& exec,
   >                                  ForwardIterator1 first, ForwardIterator1 last,
   >                                  ForwardIterator2 result,
   >                                  BinaryOperation binary_op, UnaryOperation unary_op);
   >     template<class InputIterator, class OutputIterator,
   >              class BinaryOperation, class UnaryOperation, class T>
   >       constexpr OutputIterator
   >         transform_inclusive_scan(InputIterator first, InputIterator last,
   >                                  OutputIterator result,
   >                                  BinaryOperation binary_op, UnaryOperation unary_op,
   >                                  T init);
   >     template<class ExecutionPolicy,
   >              class ForwardIterator1, class ForwardIterator2,
   >              class BinaryOperation, class UnaryOperation, class T>
   >       ForwardIterator2
   >         transform_inclusive_scan(ExecutionPolicy&& exec,
   >                                  ForwardIterator1 first, ForwardIterator1 last,
   >                                  ForwardIterator2 result,
   >                                  BinaryOperation binary_op, UnaryOperation unary_op,
   >                                  T init);

   > > -1- Let `U` be the value type of `decltype(first)``decay_t<decltype(unary_op(*first))>`.

   > > -2- […]

### 3475(i). `std::thread`'s constructor needs to be able to report general memory allocation failures

Section: 32.4.3.3 [thread.thread.constr], 32.4.4.2 [thread.jthread.cons] Status: New Submitter: Billy O'Neal III Opened: 2020-08-14 Last modified: 2020-09-06

Priority: 3

View other active issues in [thread.thread.constr].

View all other issues in [thread.thread.constr].

View all issues with New status.

Discussion:

`(j)thread`'s constructor needs to `decay-copy` the supplied parameters and callable over to the started thread through an operating system API that generally only accepts a single `void*`. The MSVC++ and libc++ implementations do this by putting the parameters in a `std::tuple` allocated from the heap, passing a pointer to that tuple through the operating system API, and leaving ownership of the parameters to the other thread.

It might be theoretically possible to introduce an additional copy and synchronization where the starting thread blocks for the started thread to make a move constructed copy of that tuple from the parameters, but that would introduce unreasonable synchronization overhead since the starting thread would have to block for all TLS initializers and similar in the started thread.

It is technically possible to implement the current design by transforming this allocation failure into `resource_unavailable_try_again`, but the description for this error in the standard is that some thread-based limitation has been reached, not a general memory limit, so that doesn't seem to meet the spirit of the requirement.

[2020-08-21; Issue processing telecon: set priority to 3]

Jonathan: I prefer Option A, but I think we need something like: "any exceptions thrown by the `decay-copy` calls, or ...".

Proposed resolution:

This wording is relative to N4861.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: The memory allocation failure results in `bad_alloc`.

1. Modify 32.4.3.3 [thread.thread.constr] as indicated:

   >     template<class F, class... Args> explicit thread(F&& f, Args&&... args);

   > > -3- Constraints: […]

   > > […]

   > > -8- Postconditions: `get_id() != id()`. `*this` represents the newly started thread.

   > > -9- Throws: `bad_alloc` if memory to transfer parameters to the new thread cannot be obtained. `system_error` if unable to start the new thread.

   > > -10- Error conditions:

   > > 1. (10.1) — `resource_unavailable_try_again` — the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.

2. Modify 32.4.4.2 [thread.jthread.cons] as indicated:

   >     template<class F, class... Args> explicit jthread(F&& f, Args&&... args);

   > > -3- Constraints: […]

   > > […]

   > > -8- Postconditions: `get_id() != id()` is `true` and `ssource.stop_possible()` is `true` and `*this` represents the newly started thread. [Note: The calling thread can make a stop request only once, because it cannot replace this stop token. — end note]

   > > -9- Throws: `bad_alloc` if memory to transfer parameters to the new thread cannot be obtained. `system_error` if unable to start the new thread.

   > > -10- Error conditions:

   > > 1. (10.1) — `resource_unavailable_try_again` — the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.

Option B: The memory allocation failure results in a `system_error` with the error condition `out_of_memory`.

1. Modify 32.4.3.3 [thread.thread.constr] as indicated:

   >     template<class F, class... Args> explicit thread(F&& f, Args&&... args);

   > > -3- Constraints: […]

   > > […]

   > > -8- Postconditions: `get_id() != id()`. `*this` represents the newly started thread.

   > > -9- Throws: `system_error` if unable to start the new thread.

   > > -10- Error conditions:

   > > 1. (10.?) — `not_enough_memory` — the system lacked memory resources to transfer parameters to the new thread.

   > > 2. (10.1) — `resource_unavailable_try_again` — the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.

2. Modify 32.4.4.2 [thread.jthread.cons] as indicated:

   >     template<class F, class... Args> explicit jthread(F&& f, Args&&... args);

   > > -3- Constraints: […]

   > > […]

   > > -8- Postconditions: `get_id() != id()` is `true` and `ssource.stop_possible()` is `true` and `*this` represents the newly started thread. [Note: The calling thread can make a stop request only once, because it cannot replace this stop token. — end note]

   > > -9- Throws: `system_error` if unable to start the new thread.

   > > -10- Error conditions:

   > > 1. (10.?) — `not_enough_memory` — the system lacked memory resources to transfer parameters to the new thread.

   > > 2. (10.1) — `resource_unavailable_try_again` — the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.

### 3486(i). `is_constructible<T[], T...>` may be misleading in C++20

Section: 21.3.5.4 [meta.unary.prop] Status: LEWG Submitter: Jonathan Wakely Opened: 2020-10-01 Last modified: 2025-10-21

Priority: 4

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with LEWG status.

Discussion:

According to the current wording, `std::is_constructible<int[], int>` should be true, because the preconditions are met (all types are complete types or unbounded arrays) and the variable definition is well-formed since C++20:

>     using T = int[];
>     T t(declval<int>()); // equiv. to int t[] = {1};

However, this doesn't construct an object of type `int[]` because it deduces the array bound from the initializers, and so constructs `int[1]`, which is not the type being asked about. It seems more logical for the trait to give a false result for an unbounded array, because it's an incomplete type, and no `int[]` can ever be constructed.

On the reflector Tim Song noted:

> On the other hand, the result is something to which an `int(&)[]` can be bound directly thanks to another C++20 change, so a lot of things might Just Work (for some definition of "Work") despite the type difference.

This seems to me a reasonable rationale for `is_constructible<int(&&)[], int>` to be true (which it is today), but not for `int[]`.

Peter Dimov replied:

> Placement new, which is often the way to construct we're interested in, is not going to work even for `T[2]`.

For example:

>     using T2 = int[2];
>     T2 x;
>     new(x) T2(1, 2); // ill-formed

We need to decide what behaviour we want here. Do we just want `is_constructible` to reflect the `T(declval<Args...>);` construct as currently specified in 21.3.5.4 [meta.unary.prop] p8, or do we want to give a more useful/meaningful answer here?

Should we revisit 21.3.5.4 [meta.unary.prop] p8 in the light of parenthesized aggregate init, so that `is_constructible<T[], T>` and `is_constructible<T[1], T>` are false?

There may be some interaction with LWG 3436(i).

[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]

Proposed resolution:

### 3487(i). Missing precondition on input and output aliasing of [numeric.ops]

Section: 26.10 [numeric.ops] Status: New Submitter: Matthias Kretz Opened: 2020-10-01 Last modified: 2020-10-02

Priority: 3

View all other issues in [numeric.ops].

View all issues with New status.

Discussion:

The algorithms `partial_sum`, `exclusive_scan`, `inclusive_scan`, `transform_exclusive_scan`, `transform_inclusive_scan`, and `adjacent_difference` with no `ExecutionPolicy` do not have a precondition "`result` is not in the range [`first`, `last`)". But they explicitly allow "`result` may be equal to `first`". This suggests the precondition got lost, because otherwise the permission is redundant.

Suggested fix: Add "`result` is not in the range [`first + 1`, `last`)." to the Preconditions paragraphs of the affected generalized numeric operations.

[2020-10-02; Issue processing telecon: Priority set to P3.]

Proposed resolution:

### 3488(i). Is `array<const int, 0>` swappable or not?

Section: 23.3.3.4 [array.special] Status: Open Submitter: Casey Carter Opened: 2020-10-01 Last modified: 2020-10-04

Priority: 3

View all issues with Open status.

Discussion:

Per 23.3.3.4 [array.special]/1, `std::array`'s non-member swap participates in overload resolution when the array has size 0 or swappable elements. The effects of non-member swap are "As if by [member swap]", but member swap's effects are simply "Equivalent to swap_ranges(begin(), end(), y.begin())" per 23.3.3.3 [array.members]/4. In effect, we've gone out of our way to ensure that `is_swappable_v<array<T, 0>>` and `swappable<array<T, 0>>` are always true despite that actually swapping such an array may be ill-formed.

It seems that the wording stops half-way to making `array<T, 0>` swappable regardless of `T`. I personally find that design distasteful - it seems a gratuitous difference between `array<T, N>` and `array<T, 0>` - but I'd prefer a consistent design over the status quo even if it's the "wrong" design.

[2020-10-02; Issue processing telecon]

Preference for Option B, and successful vote to move to Tentatively Ready. But on the reflector Tim Song pointed out a conflict with 2157(i) and question the decision. Status to Open instead. Priority set to P3 in line with 2157(i).

Proposed resolution:

Wording relative to N4861.

This resolution proposes two wording alternatives: Option A makes `array<T, 0>` swappable regardless of `T`, and the clearly superior Option B makes `array<T, N>` swappable only if `T` is swappable (i.e., regardless of `N`) removing gratuitous special-case behavior for the `N == 0` case.

Option A:

1. Change 23.3.3.3 [array.members] as follows:

   >     constexpr void swap(array& y) noexcept(N == 0 || is_nothrow_swappable_v<T>);

   -4- Effects: If `N == 0`, no effects. Otherwise, equivalent Equivalent to `swap_ranges(begin(), end(), y.begin())`.

   -5- […]

2. Also remove the now-redundant paragraph four from 23.3.3.5 [array.zero] as follows:

   > -4- Member function `swap()` shall have a non-throwing exception specification.

Option B:

1. Change 23.3.3.4 [array.special] as follows:

   >     template<class T, size_t N>
   >     constexpr void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));

   -1- Constraints: `N == 0` or `is_swappable_v<T>` is `true`.

2. Also remove paragraph four from 23.3.3.5 [array.zero] as follows:

   > -4- Member function `swap()` shall have a non-throwing exception specification.

### 3489(i). Improve `istream_view` wording

Section: 25.6.6.3 [range.istream.iterator] Status: New Submitter: Michael Schellenberger Costa Opened: 2020-10-09 Last modified: 2021-09-02

Priority: 3

View all other issues in [range.istream.iterator].

View all issues with New status.

Discussion:

While implementing `iranges::stream_view` we found some issues with the Preconditions on the member functions of `istream_view::iterator`, which are superfluous or incorrect.

1. 25.6.6.3 [range.istream.iterator] p2 reads as:

   > Preconditions: `parent_->stream_ != nullptr` is `true`.

   However in the Effects element 25.6.6.3 [range.istream.iterator] p3 it reads:

   > Effects: Equivalent to:

   > >     *parent_->stream_ >> parent_->object_;
   > >     return *this;

   For the Effects element to be valid, we implicitly require that `parent_ != nullptr`, `parent_->stream_ != nullptr` and — because we are reading from the underlying stream — `!*x.parent_->stream_`.

   Given that the Preconditions element only mentions one of the three preconditions and essentially means that we are not at the end of the stream, we should replace 25.6.6.3 [range.istream.iterator] p2 by:

   > Preconditions: `*this != default_sentinel`.

2. We should use the same precondition for 25.6.6.3 [range.istream.iterator] p4, even if it is implicit via the Effects element in 25.6.6.3 [range.istream.iterator] p5, as that requires experts knowledge of the standard.

3. The Precondition in 25.6.6.3 [range.istream.iterator] p6 is completely bogus, as accessing the cached object has no dependency on the stream. We assume it is meant that we are not at the end of the stream. Again we should change this to:

   > Preconditions: `*this != default_sentinel`.

[2020-10-14; Priority to P3 after reflector discusssion]

[2021-09-02; Jonathan comments:]

The preconditions were removed by P2325R3 approved in June 2021. Although the pointers now cannot be null, it's unclear if we want to require `fail()` to be false for the stream.

Proposed resolution:

This wording is relative to N4861.

1. Modify 25.6.6.3 [range.istream.iterator] as indicated:

   >     iterator& operator++();

   > > -2- Preconditions: `parent_->stream_ != nullptr*this != default_sentinel` is `true`.

   > > -3- Effects: Equivalent to:

   > > >     *parent_->stream_ >> parent_->object_;
   > > >     return *this;

   >     void operator++(int);

   > > -4- Preconditions: `parent_->stream_ != nullptr*this != default_sentinel` is `true`.

   > > -5- Effects: Equivalent to `++*this`.

   >     Val& operator*() const;

   > > -6- Preconditions: `parent_->stream_ != nullptr*this != default_sentinel` is `true`.

   > > -7- Effects: Equivalent to: `return parent_->object_;`

   >     friend bool operator==(const iterator& x, default_sentinel_t);

   > > -8- Effects: Equivalent to: `return x.parent_ == nullptr || !*x.parent_->stream_;`

### 3491(i). What is a "decayed type"?

Section: 17.12.6 [cmp.alg], 25.7.6.1 [range.all.general], 17.12.6 [cmp.alg] Status: New Submitter: Alisdair Meredith Opened: 2020-10-27 Last modified: 2021-10-30

Priority: 3

View other active issues in [cmp.alg].

View all other issues in [cmp.alg].

View all issues with New status.

Discussion:

Some of our newer wording for C++20 uses the term "decayed type" as if it were a defined term of art. While I have intuition for what may be intended in these cases, it turns out the "function to function pointer decay" and "array to array pointer decay" were never actually Core terms of art — having searched all standards going back as far as C++03.

We should either define this term for library use, or find a way to state our intent using existing well-defined terms of art.

Affected clauses:

- 17.12.6 [cmp.alg]: Most heavily used here.

- 25.7.6.1 [range.all.general]

- 17.12.6 [cmp.alg]

[2021-01-15; Telecon prioritization]

Set priority to 3 following reflector and telecon discussions.

Proposed resolution:

### 3493(i). The constructor of `std::function` taking an `F` is missing a constraint

Section: 22.10.17.3.2 [func.wrap.func.con] Status: New Submitter: Ville Voutilainen Opened: 2020-10-31 Last modified: 2021-08-20

Priority: 3

View all other issues in [func.wrap.func.con].

View all issues with New status.

Discussion:

In P0288, `any_invocable` is (correctly) constraining its constructor that takes an `F`:

>     template<class F> any_invocable(F&& f);

> > Let `VT` be `decay_t<F>`.

> > Constraints:

> > 1. — […]

> > 2. — `is_constructible_v<VT, F>` is `true`, and

> > 3. — […]

`std::function` doesn't do that. According to N4868, 22.10.17.3.2 [func.wrap.func.con] p8 has a constraint for Lvalue-Callable, but not for copy-constructibility. There is a precondition in p9, but that's not enough for portable well/ill-formedness.

Since this is a constructor, and we want to give the right answer to `is_constructible`/`constructible_from` queries, we should add the relevant constraint.

[2020-11-01; Daniel comments]

This issue has some overlap with LWG 2774(i).

[2021-01-15; Telecon prioritization]

Set priority to 3 following reflector and telecon discussions.

[2021-05-17; Tim comments]

The new constraint causes constraint recursion in an example like:

>     struct C {
>         explicit C(std::function<void()>); // #1
>         void operator()() {}
>     };
>     static_assert(std::is_constructible_v<C, const C&>);

Here, to determine whether a `C` can be constructed from a `const C` lvalue, the overload resolution will attempt to determine whether the constructor marked `#1` is a viable candidate, which involves a determination of whether that lvalue can be implicitly converted to a `std::function<void()>`, which, with the new constraint, requires a determination whether `C` is copy-constructible — in other words, whether it can be constructed from a `C` lvalue.

This is similar to LWG 3420(i): in both cases we have a class (`filesystem::path` there, `function` here) that is convertible from every type that are, inter alia, copy constructible, and this then results in constraint recursion when we ask whether a different type that is constructible from such a class is copy constructible.

The `C` above is reduced from an internal helper type in libstdc++. Given the ubiquity of call wrappers — types that are callable in their own right and therefore may not be able to be ruled out by the Lvalue-Callable constraint, and can also naturally have a constructor that take the wrapped function object as the argument, triggering the recursion scenario — it is not clear that there is a good way to add this constraint without causing undue breakage.

[2021-08-20; LWG telecon]

LWG requested that the constraint cited above for `move_only_function` (né `any_invocable`) be moved to a Mandates: element instead, to avoid the same constraint recursion.

Proposed resolution:

This wording is relative to N4868.

1. Modify 22.10.17.3.2 [func.wrap.func.con] as indicated:

   >     template<class F> function(F f);

   > > -8- Constraints: `F` is Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument types `ArgTypes...` and return type `R`, and `is_copy_constructible_v<F>` is `true`.

   > > -9- Preconditions: `F` meets the Cpp17CopyConstructible requirements.

   > > […]

### 3496(i). What does "uniquely associated" mean for `basic_syncbuf::emit()`?

Section: 31.11.2.4 [syncstream.syncbuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-11-10 Last modified: 2020-11-21

Priority: 3

View other active issues in [syncstream.syncbuf.members].

View all other issues in [syncstream.syncbuf.members].

View all issues with New status.

Discussion:

31.11.2.4 [syncstream.syncbuf.members] p5 says "May call member functions of `wrapped` while holding a lock uniquely associated with `wrapped`."

It's unclear what "uniquely associated" means. Is it required to be a one-to-one mapping, so that every `streambuf*` that is wrapped is associated with a different lock?

I believe the intention is only that for a given `streambuf*` every `syncbuf` that wraps it uses the same lock. The intention was that it's a valid implementation for the same lock to be used for more than one `streambuf*` (e.g. using a table of `N` locks which are indexed by a hash of the `streambuf* value)`. The current wording can be interpreted to forbid that implementation.

[2020-11-21; Reflector prioritization]

Set priority to 3 during reflector discussions.

Proposed resolution:

### 3497(i). Postconditions for `basic_syncbuf::emit()`

Section: 31.11.2.4 [syncstream.syncbuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-11-10 Last modified: 2020-11-21

Priority: 3

View other active issues in [syncstream.syncbuf.members].

View all other issues in [syncstream.syncbuf.members].

View all issues with New status.

Discussion:

31.11.2.4 [syncstream.syncbuf.members] p2 says:

> Postconditions: On success, the associated output is empty.

Are there any postconditions on failure? If part of the associated output was written, is that part still in the associated output? Will another call to `emit()` duplicate that part?

[2020-11-21; Reflector prioritization]

Set priority to 3 during reflector discussions.

Proposed resolution:

### 3499(i). Timed lockable and mutex requirements are imprecise about `duration` and `time_point`

Section: 32.2.5.4 [thread.req.lockable.timed], 32.6.4.3 [thread.timedmutex.requirements] Status: New Submitter: Tim Song Opened: 2020-11-14 Last modified: 2020-11-21

Priority: 3

View all issues with New status.

Discussion:

The timed lockable and mutex requirements currently use "`rel_time` denotes an object of an instantiation of duration, and `abs_time` denotes an object of an instantiation of `time_point`" to define the variables used to specify the timed lock functions. During LWG review of P2160R0, it was noted that this definition is deficient in two aspects:

- It doesn't allow for cv-qualification of `rel_time` and `abs_time`

- For `time_point`s, it should require that the clock is a real Cpp17Clock. We impose that requirement via 32.2.1 [thread.req.paramname], but there are no template parameters named `Clock` here.

[2020-11-21; Reflector prioritization]

Set priority to 3 during reflector discussions.

Proposed resolution:

### 3501(i). `basic_syncbuf`-related manipulators refer to some `Allocator` without defining it

Section: 31.7.6.5 [ostream.manip] Status: New Submitter: Jonathan Wakely Opened: 2020-11-15 Last modified: 2020-11-21

Priority: 3

View all other issues in [ostream.manip].

View all issues with New status.

Discussion:

From this editorial issue request:

The three `basic_syncbuf`-related manipulators `emit_on_flush`, `noemit_on_flush`, and `flush_emit` use in their Effects: elements the following wording:

> "If `os.rdbuf()` is a `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls […]

There are two problems with that wording (even when considering the helpful note following p8): First, the type `Allocator` is not defined elsewhere (e.g. it is not part of the function signature) and second, `os.rdbuf()` has type `basic_streambuf<charT, traits>*` and not any other type.

By introducing an expository `SYNCBUF` to detect `basic_syncbuf` during the work on the above mentioned editorial issue to solve these problems it turned out that the suggested wording fix would introduce an apparently normative change that the `syncbuf` type must not use a program-defined specialization.

[2020-11-21; Reflector prioritization]

Set priority to 3 during reflector discussions.

Proposed resolution:

This wording is relative to N4868.

> This proposed wording is known to be incorrect, but is nonetheless depicted to present the overall idea.

1. Modify 31.7.6.5 [ostream.manip] as indicated:

   > -1- Each instantiation of any of the function templates specified in this subclause is a designated addressable function (16.4.5.2.1 [namespace.std]).

   > -?- In this subclause, `SYNCBUF(p)` for a pointer `p` of type `B*` is determined as follows. If `*p` is a base class subobject of an object of type `S`, where `S` is a specialization generated from the `basic_syncbuf` primary template, and `is_convertible_v<S*, B*>` is `true`, then `SYNCBUF(p)` is `dynamic_cast<S*>(p)`. Otherwise, `SYNCBUF(p)` is `static_cast<void*>(nullptr)`. [Note ?: To work around the issue that the `Allocator` template argument of `S` cannot be deduced, implementations can introduce an intermediate base class to `basic_syncbuf` that manages its `emit_on_sync` flag. — end note]

   > […]

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& emit_on_flush(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -8- Effects: If `pos.rdbuf()` is not nulla `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->set_emit_on_sync(true)`. Otherwise this manipulator has no effect. [Note 1: To work around the issue that the Allocator template argument cannot be deduced, implementations can introduce an intermediate base class to `basic_syncbuf` that manages its `emit_on_sync` flag. — end note]

   > > -9- Returns: `os`.

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -10- Effects: If `pos.rdbuf()` is not nulla `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->set_emit_on_sync(false)`. Otherwise this manipulator has no effect.

   > > -11- Returns: `os`.

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -12- Effects: Calls `os.flush()`. Then, if `pos.rdbuf()` is not nulla `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->emit()`.

   > > -13- Returns: `os`.

### 3503(i). `chrono::ceil` has surprising requirement

Section: 30.5.8 [time.duration.cast] Status: New Submitter: Jonathan Wakely Opened: 2020-11-18 Last modified: 2024-09-19

Priority: 3

View all other issues in [time.duration.cast].

View all issues with New status.

Discussion:

30.5.8 [time.duration.cast] p7 requires that the return value is "The least result `t` representable in `ToDuration` for which `t >= d`".

This means that `chrono::ceil<chrono::microseconds>(chrono::duration<float, milli>(m)).count()` is required to be the smallest integer `n` such that `(float)n == m*1000.0f`, which might be less than the mathematically correct value of `m × 1000`.

(The specific values below assume `float` uses the IEEE binary32 format and default rounding, but similar problems will exist for other formats, even if the specific values are different.)

For example, if `m == 13421772.0f` then the naively expected result is `n == 13421772000`, but the standard requires `n == 13421771265`, a significantly lower value. This surprising result is a consequence of how the `chrono::ceil` spec interacts with floating-point arithmetic, due to the fact that for the integers in the range `[13421770753, 13421772799]`, only one can be exactly represented as 32-bit `float`. All but that one will be rounded to a different value when converted to `float`.

A straightforward implementation of `chrono::ceil` will produce `(long long)(13421772.0f * 1000)` which is `13421771776`, which is less than the expected result, but compares equal using the `t >= d` expression. That expression converts both operands to their `common_type`, which is `chrono::duration<float, micro>`. That means we compare `(float)13421771776 >= (13421772.0f * 1000)` which is `true`. But the spec requires an even worse result. All integers in `[13421771265, 13421771776)` are also rounded to that value when converted to `float`. That means `chrono::microseconds(13421771265)` is "the least result representable in `ToDuration` for which `t >= d`".

Meeting the "least result" requirement is impractical, and unhelpful. The straightforward result `13421771776` is already lower than the naively expected result (which is surprising for a "ceil" function). To meet the standard's requirements the implementation would have to do extra work, just to produce an even lower (and even more surprising) result.

It might be impractical to require the naively expected value to be returned (the additional work might have unacceptable performance implications), but the standard should at least permit the straightforward result instead of requiring an even worse one.

The same problem almost certainly exists for `chrono::floor` in reverse.

[2020-11-29; Reflector prioritization]

Set priority to 3 during reflector discussions.

[2024-09-19; Jonathan adds a note]

Another problem discovered by STL occurs when the result is floating-point. We can't just add 1. In fact, there is no requirement for whole-numberness.

For example, when converting from `double` to `float`:

    chrono::floor<duration<float>>(duration<double>(0.1))

This produces the result `duration<float>(-0.9f)` with the reference implementation in P0092R1, and the implementations in libstdc++, libc++, and MSVC. This is because `0.1f <= 0.1` is false, so the result is `duration<float>(0.1f - 1.0f)`, which is not the greatest value representable that is not greater than `1.0`. The correct result according to the standard would be `duration<float>(nexttoward(0.1f, -HUGE_VAL))`, but we can't use `nexttoward` for arbitrary `treat_as_floating_point` types, only for `float`, `double` and `long double`.

STL found cases where `ceil<duration<float>>(duration<double>(x))` produces a value that is lower than `x`, e.g. for `x = 13421771263.0` the result is `13421770752.0f`.

A possible resolution for this problem would be to make `ceil` and `floor` behave exactly like `duration_cast` when the result is a floating-point type. This would still permit a `ceil` that is smaller than the input (and a `floor` result that is larger) but that's just a consequence of converting to a floating-point type with less precision. We could also specify that for non-floating-point result types, the effects should be what all known implementations do. That would mean the behaviour is at least predictable and explainable, even if the result is not always the correct mathematical value.

Proposed resolution:

This wording is relative to N4988.

1. Modify 30.5.8 [time.duration.cast] as indicated:

   >     template<class ToDuration, class Rep, class Period>
   >       constexpr ToDuration floor(const duration<Rep, Period>& d);

   > -4- Constraints: `ToDuration` is a specialization of `duration`.

   > -5- Returns: The greatest result `t` representable in `ToDuration` for which `t <= d`.

   > Effects: Equivalent to:

   >     auto t = duration_cast<ToDuration>(d);
   >     if constexpr (treat_as_floating_point_v<typename ToDuration::rep>)
   >       return t;
   >     else if (t <= d)
   >       return t;
   >     else
   >       return --t;

   >     template<class ToDuration, class Rep, class Period>
   >       constexpr ToDuration ceil(const duration<Rep, Period>& d);

   > -6- Constraints: `ToDuration` is a specialization of `duration`.

   > -7- Returns: The least result `t` representable in `ToDuration` for which `t >= d`.

   > Effects: Equivalent to:

   >     auto t = duration_cast<ToDuration>(d);
   >     if constexpr (treat_as_floating_point_v<typename ToDuration::rep>)
   >       return t;
   >     else if (t >= d)
   >       return t;
   >     else
   >       return ++t;

### 3507(i). P0881R7 ("stacktrace") does not define "actual file name", "actual line number"

Section: 19.6.3.4 [stacktrace.entry.query] Status: Open Submitter: Thomas Köppe Opened: 2020-12-02 Last modified: 2023-01-11

Priority: 2

View all issues with Open status.

Discussion:

The specification of 19.6.3.4 [stacktrace.entry.query] uses the terms "presumed or actual name of the source file" and "actual line number". It makes reference to 15.12 [cpp.predefined], which introduces the term "presumed". It does not clearly define the term, but it describes how the presumed values can be modified with preprocessor directives. However, there is no definition whatsoever of "actual".

The term should either be defined, or we should strike the "actual" parts of the stacktrace wording. We should consult implementers about this.

I don't have a proposed resolution, but if we want to keep "actual", then perhaps we should define both "presumed" and "actual" in 15.12 [cpp.predefined].

[2021-01-15; Telecon prioritization]

Set priority to 2 following reflector and telecon discussions.

[2023-01-11; LWG telecon]

We want to know the original intended meaning of "actual line number" here. Presumably debuginfo stored in binaries uses a real line number in the source, not one that might have been set by a `#line` directive. The "presumed or actual" wording was added in R2 of the paper, possibly as a result of LWG review.

Proposed resolution:

### 3511(i). Clarify global permission to move

Section: 16.4.5.9 [res.on.arguments] Status: New Submitter: Gonzalo Brito Gadeschi Opened: 2020-12-08 Last modified: 2021-01-15

Priority: 3

View all other issues in [res.on.arguments].

View all issues with New status.

Discussion:

The intent of LWG 1204(i) is to allow standard library APIs accepting rvalue arguments:

- to move from their arguments, e.g., without having to specify that they might do this as part of their Effects clause, and

- to assume that rvalue arguments do not alias any pointer in the scope of the standard library API, e.g., to allow `vector`'s `push_back(T&& t)` to assume that `t` is not an element of the `vector`.

The current wording in 16.4.5.9 [res.on.arguments]/1.3 states:

> If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument.

This sentence is not clear about the scope in which the reference can be assumed to be unique, and it does not explicitly state that the function can modify the argument, e.g., to move from it.

If the scope of the "unique reference" is "whole program scope", this example:

>     void example(vector<int>& a, int* b)
>     {
>       int* c = b;            // reference to object pointed at by b
>       a.push_back(move(*b)); // UB: rvalue reference aliases c: not unique in whole-program scope
>       assert(c == b);        // FAILS: if rvalue reference to *b is unique, b is unique, and c == b is false
>     }

exhibits UB because the implementation may assume that the reference to `b` is unique, which does not hold since `c` is also a reference to `b`.

If the scope of the "unique reference" is the "function scope" of the standard library API, then the semantics of the rvalue reference argument are very similar to those of C's `restrict`. This allows aliasing optimizations, for example:

>     void std_api(int&& a, int&& b); // allowed to assume that a and b do not alias
>     int a, b, c;
>     std_api(move(a), move(b)); // OK: two unique references in std_api
>     std_api(move(c), move(c)); // UB: a and b alias

See llvm Bug 48238 for a bug tracking the implementation of these optimizations in clang.

This also allows optimizing `vector::push_back(T&& t)` since if `t` does not alias any pointer in `vector::push_back`'s scope, it also does not alias `this`, `this->data()`, `(*this)[0]`, etc.

[2021-01-15; Telecon prioritization]

Set priority to 3 following reflector and telecon discussions.

Proposed resolution:

This wording is relative to N4868.

1. Modify 16.4.5.9 [res.on.arguments] as indicated:

   > -1- Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

   > 1. (1.1) — If an argument to a function has an invalid value (such as a value outside the domain of the function or a pointer invalid for its intended use), the behavior is undefined.

   > 2. (1.2) — If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.

   > 3. (1.3) — If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argumentthe value within the function's scope and may move from it.

   >    [Example ?:

   >    >     void std_api(int&& a);
   >    >     int a;
   >    >     std_api(move(a));
   >    >     // a is in an unspecified but valid state

   >    — end example]

   >    [Example ?:

   >    >     void std_api(int&& a, int&& b);
   >    >     int a, b, c;
   >    >     std_api(move(a), move(b)); // OK: int&& a and int&& b do not alias
   >    >     std_api(move(c), move(c)); // UB: int&& a and int&& b alias

   >    — end example]

   >    [Example ?:

   >    >     std::vector<int> a = {...};
   >    >     a.push_back(move(42)); // OK: unique reference
   >    >     a.push_back(move(a[0])); // UB: (*this)[0] and rvalue argument alias

   >    — end example]

   >    [Note 1: If the parameter is a generic parameter of the form `T&&` and an lvalue of type `A` is bound, the argument binds to an lvalue reference (13.10.3.2 [temp.deduct.call]) and thus is not covered by the previous sentencethis item. — end note] [Note 2: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g., by calling the function with the argument `std::move(x))`, the program is effectively asking that function to treat that lvalue as a temporary object. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note]

### 3512(i). Incorrect exception safety guarantee for unordered containers

Section: 22.10.19 [unord.hash] Status: New Submitter: Jonathan Wakely Opened: 2020-12-28 Last modified: 2021-01-29

Priority: 3

View all other issues in [unord.hash].

View all issues with New status.

Discussion:

See this editorial issue.

22.10.19 [unord.hash] p5.4 ends with "shall not throw an exception unless `hash<Key>` is a program-defined specialization that depends on at least one program-defined type."

This seems wrong, because `hash<optional<T>>` is not a program-defined specialization, but it might throw if `hash<T>` can throw. There are also other partial specializations of `std::hash` defined in the standard library but that depend on program-defined specializations and so can throw.

[2021-01-29; reflector prioritization]

Set priority to 3 following reflector discussions. It was pointed out that this wording could be simplified if 3513(i) changes the definition of program-defined type.

Proposed resolution:

### 3513(i). Fix definition of program-defined based on its uses

Section: 3.43 [defns.prog.def.type] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-12-29 Last modified: 2021-01-30

Priority: 3

View all issues with New status.

Discussion:

Consider the following definitions:

> 3.42 [defns.prog.def.spec]
> program-defined specialization
> <library> explicit template specialization or partial specialization that is not part of the C++ standard library and not defined by the implementation

and

> 3.43 [defns.prog.def.type]
> program-defined type
> <library> non-closure class type or enumeration type that is not part of the C++ standard library and not defined by the implementation, or a closure type of a non-implementation-provided lambda expression, or an instantiation of a program-defined specialization [Note 1 to entry: Types defined by the implementation include extensions (4.1) and internal types used by the library. — end note]

A program-defined type is either a type or an instantiation. "program-defined type" is used in 16.4.5.2.1 [namespace.std] p2 to give permission to specialize standard class templates:

> Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace `std` provided that (a) the added declaration depends on at least one program-defined type and (b) the specialization meets the standard library requirements for the original template.

ISO requires that the terms in Clause 3 be substitutable with their definitions. If this were done for "program-defined type", we'd end up with "or an instantiation of a program-defined specialization". It's fine to depend on a type, but not an instantiated type, because all you need is the name of its specialization (its type) as a template argument to explicitly or partially specialize a template.

[2021-01-29; reflector prioritization]

Set priority to 3 following reflector discussions. It was pointed out that it might be easier to resolve 3512(i) if this issue changes the definition of program-defined type.

Proposed resolution:

This wording is relative to N4878.

1. Modify 3.43 [defns.prog.def.type] as indicated:

   > 3.43 [defns.prog.def.type]
   > program-defined type
   > <library> non-closure class type or enumeration type that is not part of the C++ standard library and not defined by the implementation, or a closure type of a non-implementation-provided lambda expression, or an instantiationa name of a program-defined specialization or instantiation thereof [Note 1 to entry: Types defined by the implementation include extensions (4.1 [intro.compliance]) and internal types used by the library. — end note]

### 3516(i). `thread::id` spaceship may be inconsistent with equality

Section: 32.4.3.2 [thread.thread.id] Status: New Submitter: Casey Carter Opened: 2021-01-26 Last modified: 2021-03-12

Priority: 3

View all other issues in [thread.thread.id].

View all issues with New status.

Discussion:

32.4.3.2 [thread.thread.id]/5-7 specify the behavior of `==` and `<=>` for `std::thread::id`:

>     bool operator==(thread::id x, thread::id y) noexcept;

> > -5- Returns: `true` only if `x` and `y` represent the same thread of execution or neither `x` nor `y` represents a thread of execution.

>     strong_ordering operator<=>(thread::id x, thread::id y) noexcept;

> > -6- Let `P(x, y)` be an unspecified total ordering over `thread::id` as described in 25.8.

> > -7- Returns: `strong_ordering::less` if `P(x, y)` is `true`. Otherwise, `strong_ordering::greater` if `P(y, x)` is `true`. Otherwise, `strong_ordering::equal`.

"Unspecified total ordering" provides too much freedom, since it does not require that `!P(x, y)` holds when `x` and `y` both represent the same thread of execution or both represent no thread of execution. A conforming implementation could return `strong_ordering::equal` from `<=>` for a pair of `thread::id` values for which `==` returns `false`. We should guarantee consistency of `==` and `<=>` for `thread::id` to preserve sanity of the programming model.

[2021-03-12; Reflector poll]

Set priority to 3 following reflector poll.

Proposed resolution:

This wording is relative to N4878.

1. Modify 32.4.3.2 [thread.thread.id] as indicated:

   >     strong_ordering operator<=>(thread::id x, thread::id y) noexcept;

   > > -6- Let `P(x, y)` be an unspecifieda total ordering over `thread::id` as described in 26.8 [alg.sorting], unspecified except that `P(x, y)` is `false` when `x` and `y` both represent the same thread of execution, or when neither represents a thread of execution.

   > > -7- Returns: `strong_ordering::less` if `P(x, y)` is `true`. Otherwise, `strong_ordering::greater` if `P(y, x)` is `true`. Otherwise, `strong_ordering::equal`.

### 3531(i). LWG 3025 broke previous valid code

Section: 23.4.3.1 [map.overview], 23.4.4.1 [multimap.overview], 23.5.3.1 [unord.map.overview], 23.5.4.1 [unord.multimap.overview] Status: New Submitter: Mike Spertus Opened: 2021-03-09 Last modified: 2021-04-20

Priority: 3

View all other issues in [map.overview].

View all issues with New status.

Discussion:

The resolution for LWG 3025(i) enabled code like the following to be accepted:

>     map m1{{pair{1, 2}, {3, 4}}, less<int>()};

but breaks code that had been previously working like the following

>     using value_type = pair<const int, int>;

>     map m2{{value_type{1, 2}, {3, 4}}, less<int>()};

as shown on godbolt.

[Acknowledgment to Tim Song and Arthur O'Dwyer for independently pointing out this case on the LWG mailing list]

[2021-04-20; Reflector poll]

Priority set to 3. Three preferences expressed for Option B, none for A.

Proposed resolution:

This wording is relative to N4878.

> We present two partial wording options for `std::map`, denoted by (A) and (B) below. If the committee accepts one of them, we will complete them to all key-value containers.

1. (A) Wording option 1: In this option, we restore the deduction guide that was removed in LWG 3025(i) while maintaining the one that was added, demonstrates this working.

   1. Modify 23.4.3.1 [map.overview] as indicated:

      >     […]
      >     template<class Key, class T, class Compare = less<Key>,
      >              class Allocator = allocator<pair<const Key, T>>>
      >       map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
      >         -> map<Key, T, Compare, Allocator>;

      >     template<class Key, class T, class Compare = less<Key>,
      >              class Allocator = allocator<pair<const Key, T>>>
      >       map(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
      >         -> map<Key, T, Compare, Allocator>;

      >     template<class InputIterator, class Allocator>
      >       map(InputIterator, InputIterator, Allocator)
      >         -> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>,
      >                less<iter-key-type<InputIterator>>, Allocator>;

      >     template<class Key, class T, class Allocator>
      >       map(initializer_list<pair<Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>;

      >     template<class Key, class T, class Allocator>
      >       map(initializer_list<pair<const Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>;
      >     […]

2. (B) Wording option 2: This one follows Tim Song's suggestion: "It seems that the cleanest fix is to 1) disallow the `initializer_list<value_type>` constructors from being used for CTAD, and 2) change the guides to use `remove_const_t<Key>`." This change has been tested locally with g++ similar to the above godbolt.

   1. Modify 23.4.3.1 [map.overview] as indicated:

      >     […]
      >     // types
      >     using key_type = Key;
      >     using mapped_type = T;
      >     using value_type = type_identity_t<pair<const Key, T>>;
      >     […]

      >     template<class Key, class T, class Compare = less<remove_const_t<Key>>,
      >              class Allocator = allocator<pair<const Key, T>>>
      >       map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
      >         -> map<remove_const_t<Key>, T, Compare, Allocator>;

      >     template<class InputIterator, class Allocator>
      >       map(InputIterator, InputIterator, Allocator)
      >         -> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>,
      >                less<iter-key-type<InputIterator>>, Allocator>;

      >     template<class Key, class T, class Allocator>
      >       map(initializer_list<pair<Key, T>>, Allocator)
      >         -> map<remove_const_t<Key>, T, less<remove_const_t<Key>>, Allocator>;

      >     […]

### 3534(i). `ranges::set_intersection` and `ranges::set_difference` algorithm requirements are too strict

Section: 26.8.7.4 [set.intersection], 26.8.7.5 [set.difference] Status: LEWG Submitter: Alexander Bessonov Opened: 2021-03-16 Last modified: 2021-04-20

Priority: 3

View all other issues in [set.intersection].

View all issues with LEWG status.

Discussion:

The `std::mergeable` concept requires elements of both source ranges to be copyable to the output iterator, while the standard specifically tells that both algorithms `ranges::set_intersection` and `ranges::set_difference` only use the first range as the source of elements to be copied into output. The following code snippet illustrates the problem:

>     #include <vector>
>     #include <ranges>
>     #include <algorithm>
>     #include <cassert>

>     int main()
>     {
>       std::vector<std::pair<int, int>> v1;
>       std::vector<int> v2;

>       assert(std::ranges::is_sorted(v1));
>       assert(std::ranges::is_sorted(v2));

>       std::vector<std::pair<int, int>> v3;

>       // Compilation error on the following line:
>       std::ranges::set_intersection(v1, v2, std::back_inserter(v3),
>         std::less{}, [](const auto& p) { return p.first; });
>     }

The proposed solution is to introduce a new concept. It could be declared "exposition-only" and is worded `half-mergeable` below:

>     template<class I1, class I2, class Out, class R = ranges::less,
>     	     class P1 = identity, class P2 = identity>
>       concept half-mergeable =
>         input_iterator<I1> &&
>         input_iterator<I2> &&
>         weakly_incrementable<Out> &&
>         indirectly_copyable<I1, Out> &&
>         // indirectly_copyable<I2, Out> && <— this line removed
>         indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>;

After such template is introduced, `std::mergeable` may be defined based on it:

>     template<class I1, class I2, class Out, class R = ranges::less,
>     	     class P1 = identity, class P2 = identity>
>       concept mergeable = half-mergeable<I1, I2, Out, R, P1, P2> &&
>         indirectly_copyable<I2, Out>;

See also the related discussion on reddit.

[2021-04-20; Reflector poll]

Priority set to 3. Send to LEWG.

Proposed resolution:

This wording is relative to N4878.

1. Modify 24.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >       […]

   >       // 24.3.7.7 [alg.req.mergeable], concept mergeable
   >       template<class I1, class I2, class Out,
   >           class R = ranges::less, class P1 = identity, class P2 = identity>
   >         concept half-mergeable = see below; // exposition only

   >       template<class I1, class I2, class Out,
   >           class R = ranges::less, class P1 = identity, class P2 = identity>
   >         concept mergeable = see below;

   >      […]

2. Modify 24.3.7.7 [alg.req.mergeable] as indicated:

   > 23.3.7.7 Concept `mergeable` [alg.req.mergeable]

   > -1- The `mergeable` concept specifies the requirements of algorithms that merge sorted sequences into an output sequence by copying elements.

   > >     template<class I1, class I2, class Out, class R = ranges::less,
   > >              class P1 = identity, class P2 = identity>
   > >       concept half-mergeable = // exposition only
   > >         input_iterator<I1> &&
   > >         input_iterator<I2> &&
   > >         weakly_incrementable<Out> &&
   > >         indirectly_copyable<I1, Out> &&
   > >         indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>;

   > >     template<class I1, class I2, class Out, class R = ranges::less,
   > >              class P1 = identity, class P2 = identity>
   > >       concept mergeable =
   > >         half-mergeable<I1, I2, Out, R, P1, P2> &&
   > >         input_iterator<I1> &&
   > >         input_iterator<I2> &&
   > >         weakly_incrementable<Out> &&
   > >         indirectly_copyable<I1, Out> &&
   > >         indirectly_copyable<I2, Out> &&
   > >         indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>;

3. Modify 26.8.7.4 [set.intersection] as indicated:

   >     […]
   >     template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2,
   >              weakly_incrementable O, class Comp = ranges::less,
   >              class Proj1 = identity, class Proj2 = identity>
   >       requires half-mergeablemergeable<I1, I2, O, Comp, Proj1, Proj2>
   >       constexpr ranges::set_intersection_result<I1, I2, O>
   >         ranges::set_intersection(I1 first1, S1 last1, I2 first2, S2 last2, O result,
   >                                  Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});
   >     template<input_range R1, input_range R2, weakly_incrementable O,
   >              class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
   >       requires half-mergeablemergeable<iterator_t<R1>>,
   >         iterator_t<R2>, O, Comp, Proj1, Proj2>
   >       constexpr ranges::set_intersection_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>, O>
   >         ranges::set_intersection(R1&& r1, R2&& r2, O result,
   >                                  Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});

   > > […]

   > > -6- Remarks: Stable (16.4.6.8 [algorithm.stable]). If `[first1, last1)` contains `m` elements that are equivalent to each other and `[first2, last2)` contains `n` elements that are equivalent to them, the first `min(m, n)` elements are copied from the first range to the output range, in order.

4. Modify 26.8.7.5 [set.difference] as indicated:

   >     […]
   >     template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2,
   >              weakly_incrementable O, class Comp = ranges::less,
   >              class Proj1 = identity, class Proj2 = identity>
   >       requires half-mergeablemergeable<I1, I2, O, Comp, Proj1, Proj2>
   >       constexpr ranges::set_difference_result<I1, O>
   >         ranges::set_difference(I1 first1, S1 last1, I2 first2, S2 last2, O result,
   >                                Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});
   >     template<input_range R1, input_range R2, weakly_incrementable O,
   >              class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
   >       requires half-mergeablemergeable<iterator_t<R1>>, iterator_t<R2>, O, Comp, Proj1, Proj2>
   >       constexpr ranges::set_difference_result<borrowed_iterator_t<R1>, O>
   >         ranges::set_difference(R1&& r1, R2&& r2, O result,
   >                                Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});

   > > […]

   > > -6- Remarks: If `[first1, last1)` contains `m` elements that are equivalent to each other and `[first2, last2)` contains `n` elements that are equivalent to them, the last `max(m - n, 0)` elements from `[first1, last1)` is copied to the output range, in order.

### 3538(i). §[library.c] C library functions are not addressable

Section: 16.2 [library.c] Status: New Submitter: Jiang An Opened: 2021-03-29 Last modified: 2021-04-20

Priority: 2

View all issues with New status.

Discussion:

P0551R3 has made almost all standard library functions non-addressable, including all functions from the C standard library. However, C17 (and the latest C23 working draft) explicitly allows taking address of a C standard library function in 7.1.4/1.

Should we require something like "every function from the C standard library is addressable unless it is overloaded", in order to minimize the incompatibility with C? Or explicitly say something in C.8 [diff.library] if such requirement is not wanted?

[2021-04-20; Reflector poll]

Priority set to 2.

Proposed resolution:

### 3550(i). Names reserved by C for standard library not reserved by C++

Section: 16.4.5.3 [reserved.names] Status: New Submitter: Hubert Tong Opened: 2021-05-10 Last modified: 2021-05-20

Priority: 3

View all other issues in [reserved.names].

View all issues with New status.

Discussion:

The C standard has a section called "Future library directions". This is subclause 7.31 in C17, which is the version that the C++ working draft references. C reserves the names described in that subclause for its standard library, allowing C library vendors to introduce some names from future standards as conforming, "orthogonal" extensions. ISO C++ does not appear to reserve these names in a clear way. In particular, 16.4.5.3 [reserved.names] has wording about names and function signatures "from the C standard library declared with external linkage", but C's "Future library directions" describes names that "may be added to the declarations in [some] header[s]".

Extra clarity regarding whether these names are actually intended to be reserved by C++ would be appreciated.

[2021-05-20; Reflector poll]

Priority set to 3.

Proposed resolution:

### 3556(i). Specification of when semantic constraints are imposed by use of concepts is unclear

Section: 16.3.2.3 [structure.requirements] Status: New Submitter: Tim Song Opened: 2021-05-23 Last modified: 2021-05-26

Priority: 3

View all other issues in [structure.requirements].

View all issues with New status.

Discussion:

16.3.2.3 [structure.requirements] p9 says:

> A declaration may explicitly impose requirements through its associated constraints (13.5.3 [temp.constr.decl]). When the associated constraints refer to a concept (13.7.9 [temp.concept]), the semantic constraints specified for that concept are additionally imposed on the use of the declaration.

There are at least two issues with this wording:

1. First, "associated constraints" is a Core term that refers to the constraints on a declaration after normalization, at which point direct uses of concepts have been decomposed into the constituent atomic constraints and are no longer visible.

2. Second, "refers to" is too vague. Does `!C<T>` "refer to" `C` and impose its semantic constraints? Does `C1<T> || C2<T>` "refer to" both `C1` and `C2` and therefore impose the semantic constraints of both?

[2021-05-26; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3577(i). Merging an (unordered) associative container with itself

Section: 23.2.7.1 [associative.reqmts.general], 23.2.8.1 [unord.req.general] Status: New Submitter: Joaquín M López Muñoz Opened: 2021-08-04 Last modified: 2024-01-29

Priority: 3

View other active issues in [associative.reqmts.general].

View all other issues in [associative.reqmts.general].

View all issues with New status.

Discussion:

For the expression `a.merge(a2)`, it is not explicitly stated whether `a2` can be the same object as `a`. libstdc++-v3 and libc++ seemingly assume this is not allowed, as the following code produces an infinite loop with both standard library implementations:

>     #include <set>

>     int main()
>     {
>       std::multiset<int> c={0, 0};
>       c.merge(c);
>     }

A strict reading of postconditions seems to ban the case where `a` and `a2` are the same:

- 23.2.7.1 [associative.reqmts.general]: "Iterators referring to the transferred elements […] now behave as iterators into `a`, not into `a2`": if `a` and `a2` are the same, a transferred iterator can't be both an iterator to `a` and not an iterator to `a2`.

- 23.2.8.1 [unord.req.general]: "Iterators referring to the transferred elements and all iterators referring to `a` will be invalidated, but iterators to elements remaining in `a2` will remain valid": if `a` and `a2` are the same, an iterator can't both be invalidated and remain valid.

Even if a provision is made that, when `a` and `a2` are the same, no elements are transferred by convention, 23.2.8.1 [unord.req.general] would still implicitly ban the case, as all iterators would be invalidated but the iterators to the remaining elements (again, all iterators) would remain valid, which is contradictory.

For context, analogous operations for `std::list` take inconsistent approaches:

- `splice(const_iterator position, list& x)` requires that source and destination be not the same.

- `splice(const_iterator position, list& x, const_iterator i)` implicitly allows `addressof(x) == this`, as the case `position == i` is taken care of.

- `std::list::merge` explicitly allows the case `addressof(x) == this` (resulting in a no-op).

[2021-08-20; Reflector poll]

Set priority to 3 after reflector poll. Tim Song commented: "I think the current PR of LWG2414(i) bans this code, but we might want to have consistency with `list::merge` instead."

Proposed resolution:

### 3582(i). Unclear where `std::async` exceptions are handled

Section: 32.10.9 [futures.async] Status: New Submitter: Jonathan Wakely Opened: 2021-08-23 Last modified: 2021-09-30

Priority: 3

View all other issues in [futures.async].

View all issues with New status.

Discussion:

32.10.9 [futures.async] (3.1) says:

> Any exception propagated from the execution of `invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)` is stored as the exceptional result in the shared state.

It's not clear whether this includes the evaluation of the `decay-copy` calls in the calling thread, or only the invocation of invoke with the results of those `decay-copy` calls.

A literal reading suggests that any exceptions from any part of that expression should be stored in the shared state. All of libstdc++, libc++ and MSVC only store exceptions from the call to `invoke`, not the calls to `decay-copy`. Exceptions from the `decay-copy` calls are propagated to the caller of `std::async`. We should clarify that that's what the standard means.

[2021-09-20; Reflector poll]

Set priority to 3 after reflector poll.

[2021-09-20; Jonathan updates wording to change the Throws: and attempt to align the Effects: with the deferred function case. ]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4892.

> 1. Modify 32.10.9 [futures.async] as indicated:

>    >     template<class F, class... Args>
>    >       [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
>    >         async(F&& f, Args&&... args);
>    >     template<class F, class... Args>
>    >       [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
>    >         async(launch policy, F&& f, Args&&... args);

>    > > -2- Mandates: […]

>    > > -3- Effects: The first function behaves the same as a call to the second function with a `policy` argument of `launch::async | launch::deferred` and the same arguments for `F` and `Args`. The second function creates a shared state that is associated with the returned `future` object. The further behavior of the second function depends on the `policy` argument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):

>    > > 1. (3.1) — If `launch::async` is set in `policy`, calls `invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)` (22.10.4 [func.require], 32.4.3.3 [thread.thread.constr]) as if in a new thread of execution represented by a `thread` object with the calls to `decay-copy` being evaluated in the thread that called `async`. Any return value is stored as the result in the shared state. Any exception propagated from the execution of `invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)`call to `invoke` is stored as the exceptional result in the shared state. [Note ?: Exceptions from the `decay-copy` calls are propagated to the caller. — end note] The `thread` object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

>    > > 2. […]

Proposed resolution:

This wording is relative to N4892.

1. Modify 32.10.9 [futures.async] as indicated:

   >     template<class F, class... Args>
   >       [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
   >         async(F&& f, Args&&... args);
   >     template<class F, class... Args>
   >       [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>>
   >         async(launch policy, F&& f, Args&&... args);

   > > -2- Mandates: […]

   > > -3- Effects: The first function behaves the same as a call to the second function with a `policy` argument of `launch::async | launch::deferred` and the same arguments for `F` and `Args`. The second function creates a shared state that is associated with the returned `future` object. The further behavior of the second function depends on the `policy` argument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):

   > > 1. (3.1) — If `launch::async` is set in `policy`, calls `invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)` (22.10.4 [func.require], 32.4.3.3 [thread.thread.constr]) as if in a new thread of execution represented by a `thread` object with the calls to `decay-copy` being evaluated in the thread that called `async`. Any return value is stored as the result in the shared state. Any exception propagated from the execution of `invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args)...)std::move(g), std::move(xyz))` is stored as the exceptional result in the shared state, where `g` is the result of `decay-copy(std::forward<F>(f))` and `xyz` is the result of `decay-copy(std::forward<Args>(args))...` . [Note ?: Exceptions from the `decay-copy` calls are propagated to the caller. — end note] The `thread` object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

   > > 2. […]

   > > […]

   > > -6- Throws: `system_error` if `policy == launch::async` and the implementation is unable to start a new thread; `std::bad_alloc` if memory for the internal data structures cannot be allocated; or any exception thrown by the initialization of the objects returned by the `decay-copy` calls.

### 3583(i). Clarify if/when short circuiting applies to conditions in Constraints: elements

Section: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Jonathan Wakely Opened: 2021-08-23 Last modified: 2021-09-20

Priority: 3

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with New status.

Discussion:

We do not specify whether or not short-circuiting is expected to happen for individual conditions stated in a Constraints: element. For example, 22.4.4.2 [tuple.cnstr] p12 says:

> Constraints: `sizeof...(Types)` equals `sizeof...(UTypes)` and `sizeof...(Types) ≤ 1` and `is_constructible_v<Ti , Ui>` is `true` for all `i`.

It's not even possible to test the `is_constructible` part unless the first part is true, so presumably it is expected that the `sizeof...` expressions are tested first, and so the `is_constructible` traits don't even need to be instantiated.

In some cases it might be user observable whether short circuiting happens, as whether later conditions are tested might affect whether errors outside the immediate context are permitted or not.

We should clarify what is intended, and refactor any Constraints: elements that are special cases where something different is required.

[2021-09-20; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3584(i). Clarify common comparison category conversions

Section: 17.12.3 [cmp.common] Status: Ready Submitter: Peter Brett Opened: 2021-08-23 Last modified: 2026-06-15

Priority: 3

View all issues with Ready status.

Discussion:

17.12.3 [cmp.common]/1 says:

> The type `common_comparison_category` provides an alias for the strongest comparison category to which all of the template arguments can be converted.

A naive reader like me might interpret this as meaning that (1) you attempt to convert the template arguments to comparison categories and then (2) obtain the strongest among them.

However, the intent is in fact to realize the common comparison type notion from 11.10.3 [class.spaceship]/4. To obtain a non-`void` result, all the template arguments must be comparison categories, rather than convertible to comparison categories.

17.12.3 [cmp.common]/2 mildly contradicts the first paragraph:

> Remarks: The member typedef-name `type` denotes the common comparison type (11.10.3 [class.spaceship]) of `Ts...`, the expanded parameter pack, or `void` if any element of `Ts` is not a comparison category type.

It more precisely states the behaviour, cross-references 11.10.3 [class.spaceship], and uses the correct core terminology for the metafunction that the template represents.

Suggested resolution;

Delete 17.12.3 [cmp.common]/1, because it does not provide any information not already more precisely included in 17.12.3 [cmp.common]/2.

[2021-09-20; Reflector poll]

Set priority to 3 after reflector poll.

[2021-09-20; Reflector poll]

Jens suggests alternative wording.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4892.

> 1. Modify 17.12.3 [cmp.common] as indicated:

>    > -1- The type `common_comparison_category` provides an alias for the strongest comparison category to which all of the template arguments can be converted. [Note 1: A comparison category type is stronger than another if they are distinct types and an instance of the former can be converted to an instance of the latter. — end note]

>    >     template<class... Ts>
>    >     struct common_comparison_category {
>    >       using type = see below;
>    >     };

>    > -2- Remarks: The member typedef-name `type` denotes the common comparison type (11.10.3 [class.spaceship]) of `Ts...`, the expanded parameter pack, or `void` if any element of `Ts` is not a comparison category type.

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N4892.

1. Modify 17.12.3 [cmp.common] as indicated:

   > -1- The type `common_comparison_category` provides an alias for the strongest comparison category to which all of the template arguments can be converted among all the template arguments. [Note 1: A comparison category type is stronger than another if they are distinct types and an instance of the former can be converted to an instance of the latter. — end note]

   >     template<class... Ts>
   >     struct common_comparison_category {
   >       using type = see below;
   >     };

   > -2- Remarks: The member typedef-name `type` denotes the common comparison type (11.10.3 [class.spaceship]) of `Ts...`, the expanded parameter pack, or `void` if any element of `Ts` is not a comparison category type.

### 3586(i). Formatting character alignment inconsistencies

Section: 28.5.2.2 [format.string.std] Status: New Submitter: Mark de Wever Opened: 2021-09-05 Last modified: 2021-12-04

Priority: 2

View other active issues in [format.string.std].

View all other issues in [format.string.std].

View all issues with New status.

Discussion:

The alignment options specified in 28.5.2.2 [format.string.std], Table [tab:format.align] causes an inconsistency when formatting characters. The output differs depending on whether an integer is formatted using a character presentation type or when using a character directly:

>     format("{:3}", '*'); -> "*  " // aligned at the start of the available space
>     format("{:3c}", 42); -> "  *" // aligned at the end of the available space

I expect both calls to return the same value: `"* "`. The current wording mixes the type and the presentation type. To me, it seems clearer to adjust to wording to only use the presentation type. Another approach would be adjusting the wording to add an exception when an integer type uses the character presentation.

[2021-09-20; Reflector poll]

Set priority to 2 after reflector poll.

Victor said "It mostly looks correct but I think the wording needs a bit more work because we don't mention arithmetic presentation types anywhere."

[2021-11-29; Daniel comments]

This issue touches the same wording area as LWG 3644(i) does.

Proposed resolution:

This wording is relative to N4892.

1. Modify 28.5.2.2 [format.string.std], Table [tab:format.align], as indicated:

   > Table 59 — Meaning of align options [tab:format.align]
   > Option | Meaning
   > --- | ---
   > `<` | Forces the field to be aligned to the start of the available space. This is the default when the presentation type is a non-arithmetic typefor non-arithmetic types, `charT`, and `bool`, unless an integer presentation type is specified.
   > `>` | Forces the field to be aligned to the end of the available space. This is the default when the presentation type is an arithmetic typefor arithmetic types other than `charT` and `bool` or when an integer presentation type is specified.
   > `[…]`

### 3587(i). `std::three_way_comparable_with<T, U, void>` can be satisfied but can't be modeled

Section: 17.12.4 [cmp.concept] Status: New Submitter: Jiang An Opened: 2021-09-06 Last modified: 2021-10-23

Priority: 3

View all other issues in [cmp.concept].

View all issues with New status.

Discussion:

Due to the current specification of `std::common_comparison_category`, `compare-as<void, void>` is satisfied. And thus given two types `T` and `U` with regular `==`, `!=`, `<`, `>`, `<=`, and `>=` but irregular `<=>` that has return type `void`, `std::three_way_comparable_with<T, U, void>` is satisfied.

However, in this situation `std::three_way_comparable_with<T, U, void>` can't be modeled, because `Cat(t <=> u) == Cat(C(t) <=> C(u))` is ill-formed if `Cat` is `void` (see this godbolt link).

I don't think this issue has any impact on meaningful codes, but it seems to be a hole in the current specification.

It seems that `compares-as<void, void>` should be changed to `false`. `compares-as<NonComparisonCategoryType, void>` might need refinement.

(Note: The same problem does not exists for `std::three_way_comparable`, because `std::three_way_comparable<T, void>` can be modeled if the `<=>` on `T` has "regular" definition or its return type is not a comparison category type, but the return value has "regular" semantics)

[2021-09-20; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3602(i). `reverse_iterator`'s converting assignment is overconstrained

Section: 24.5.1.4 [reverse.iter.cons], 24.5.4.4 [move.iter.cons] Status: New Submitter: Hewill Kang Opened: 2021-09-26 Last modified: 2021-10-14

Priority: 3

View all other issues in [reverse.iter.cons].

View all issues with New status.

Discussion:

In order to remove the incorrect bi-convertibility of `reverse_iterator<int*>` and `reverse_iterator<const int*>`, LWG 3435(i) adds two constraints to `reverse_iterator`'s converting assignment, namely `convertible_to<const U&, Iterator>` and `assignable_from<Iterator&, const U&>`, but since this function only assigns `u.current` to `current`, there is no need to require `convertible_to<const U&, Iterator>` — the latter is sufficient.

We should remove this constraint and be consistent with the `move_sentinel` and `counted_iterator`' converting assignment.

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

[Tim Song commented:]

This was intentional, but I think we missed the fact that `counted_iterator` did something else already. These should probably be made consistent one way or another.

[Tomasz Kamiński commented]

The `move_iterator`/`reverse_iterator` were present before C++20, and this change restores their compatibility with C++17 code, where only assignment was required. They are materially different from adapters introduced with C++20, and I believe we should put more weight into backward compatibility than consistency with newer iterator wrappers.

Proposed resolution:

This wording is relative to N4892.

1. Modify 24.5.1.4 [reverse.iter.cons] as indicated:

   >     template<class U>
   >       constexpr reverse_iterator&
   >         operator=(const reverse_iterator<U>& u);

   > > -5- Constraints: `is_same_v<U, Iterator>` is `false`, `const U&` models `convertible_to<Iterator>`, and `assignable_from<Iterator&, const U&>` is modeled.

   > > -6- Effects: Assigns `u.current` to `current`.

   > > -7- Returns: `*this`.

2. Modify 24.5.4.4 [move.iter.cons] as indicated:

   > [Drafting note: As drive-by fix a missing "Returns: `*this`" has been added as well.]

   >     template<class U> constexpr move_iterator& operator=(const move_iterator<U>& u);

   > > -5- Constraints: `is_same_v<U, Iterator>` is `false`, `const U&` models `convertible_to<Iterator>`, and `assignable_from<Iterator&, const U&>` is modeled.

   > > -6- Effects: Assigns `u.current` to `current`.

   > > -?- Returns: `*this`.

### 3603(i). Matching of null characters by regular expressions is underspecified

Section: 28.6.7.2 [re.regex.construct], 28.6.10 [re.alg] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14

Priority: 3

View other active issues in [re.regex.construct].

View all other issues in [re.regex.construct].

View all issues with New status.

Discussion:

ECMAScript says that `\0` is an ordinary character and can be matched. POSIX says the opposite:

"The interfaces specified in POSIX.1-2017 do not permit the inclusion of a NUL character in an RE or in the string to be matched. If during the operation of a standard utility a NUL is included in the text designated to be matched, that NUL may designate the end of the text string for the purposes of matching."

So does that mean `std::regex{"", 1, regex::basic}` should throw an exception?

And `std::regex_match(string{"a\0b", 3}, regex{"a.b", regex::basic})` should fail?

The POSIX rule is because those interfaces are specified with NTBS arguments, so there's no way to distinguish `"a\0b"` and `"a"`. The C++ interfaces could allow it, but we never specify any divergence from POSIX, so presumably the rule still applies. Is that what was intended and is it what we want?

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3604(i). What is the effect of an invalid value of type `syntax_option_type`?

Section: 28.6.7.2 [re.regex.construct], 28.6.4.2 [re.synopt] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14

Priority: 3

View other active issues in [re.regex.construct].

View all other issues in [re.regex.construct].

View all issues with New status.

Discussion:

28.6.4.2 [re.synopt] says:

> A valid value of type `syntax_option_type` shall have at most one of the grammar elements `ECMAScript`, `basic`, `extended`, `awk`, `grep`, `egrep`, set.

But then we never say what happens if an invalid value is used.

What does `std::regex(".", std::regex::grep|std::regex::awk)` do? Is it undefined? Does it throw?

It seems reasonable for `basic_regex` constructors to throw if `f` is not a valid value, i.e. for each non-default constructor:

> Throws: `regex_error` if `f` is not a valid value, or if […] is not a valid regular expression according to the grammar specified by `f`.

However, there is no `regex_constants::error_code` value suitable for this error condition.

Also, the constructors say "Postconditions: `flags()` returns `f`." This prevents an implementation from storing `f|ECMAScript` in `flags()` if no grammar element is present in `f`. This seems like an unnecessary restriction, and forces implementations to do extra work to check if the `ECMAScript` grammar is in use. Arguably, it would even be better to require implementations to set `ECMAScript` in `flags()` if no grammar element was set in the flags passed to the constructor. This problem was introduced by LWG 2330(i).

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3605(i). `regex_constants::match_prev_avail` is underspecified

Section: 28.6.4.3 [re.matchflag] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14

Priority: 3

View all other issues in [re.matchflag].

View all issues with New status.

Discussion:

The standard doesn't say what it means if `match_prev_avail` is set. Table [tab:re.matchflag] says:

> `--first` is a valid iterator position. When this flag is set the flags `match_not_bol` and `match_not_bow` shall be ignored by the regular expression algorithms (28.6.10 [re.alg]) and iterators (28.6.11 [re.iter]).

What difference does it make whether `--first` is a valid iterator position or not?

What behaviour is changed when it's a valid iterator position? The standard doesn't say anything else about that.

When do the regex algorithms care about `--first`?

Examples like `regex_match("xa"+1, regex("^a"), match_prev_avail)` and `regex_match("xa"+1, regex("\\ba"), match_prev_avail)` are presumably supposed to inspect the character at `--first` to determine if there is a match. The standard doesn't specify that `*--first` is ever inspected, only that it's a valid character (which is a useless guarantee if nothing looks at it).

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3606(i). Missing `regex_traits::locale_type` requirements

Section: 28.6.2 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2021-09-28 Last modified: 2021-10-14

Priority: 3

View other active issues in [re.req].

View all other issues in [re.req].

View all issues with New status.

Discussion:

Why is `locale_type` part of the regular expression traits requirements in 28.6.2 [re.req]? When would `locale_type` not be `std::locale`? What are the requirements on the type? Does it have to provide exactly the same interface as `std::locale`, or just some unspecified interface that a custom regex traits type needs from it? Why is none of this specified?

Currently the only requirement on `locale_type` in the standard is that it's copy constructible. Clearly it needs to be default constructible as well, otherwise you can't construct a `basic_regex`, since none of them allows passing in a locale, so they have to default construct it (see also LWG 2431(i)).

The other requirements on `locale_type` are a mystery. Why do we allow custom locale types, but not say anything about what they should do? Can we just require `locale_type` to be `std::locale`? Is anybody really going to use `boost::locale` with `std::basic_regex`, when they could just use `boost::basic_regex` instead?

Why does the regular expression traits requirements table say that `imbue` and `getloc` talk about the locale used, "if any". How would there not be one already?

Why is imbuing a locale into a `basic_regex` a separate operation from compiling the regular expression pattern? Is the following supposed to change the compiled regex?

>     std::regex r("[a-z]");
>     r.imbue(std::locale("en_GB.UTF-8"));

Hasn't the regex constructor already made use of the locale to compile the `"[a-z]"` pattern, and so changing the locale is too late? So do we need to do the following to compile the regex with a specific locale?

>     std::regex r;
>     r.imbue(std::locale("en_GB.UTF-8"));
>     r.assign("[a-z]");

Why require two-stage initialization like this, is it just so that we appear consistent with the `imbue`/`getloc` API of `std::ios_base`? It works for `ios_base`, because the new locale is effective after imbuing it, but for `basic_regex` the pattern has already been compiled using the old locale and imbuing a new one can't change that. Is the `basic_regex` supposed to store the pattern and recompile it after `imbue`, or is this just an inappropriate API for `basic_regex`?

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3608(i). `convertible_to` and temporary-bound references

Section: 18.4.4 [concept.convertible] Status: New Submitter: Tim Song Opened: 2021-09-28 Last modified: 2021-10-23

Priority: 3

View other active issues in [concept.convertible].

View all other issues in [concept.convertible].

View all issues with New status.

Discussion:

The semantic requirements of `convertible_to` express implicit conversion by means of a function:

>     To test(FromR (&f)()) {
>       return f();
>     }

and it requires that `static_cast<To>(f())` be equal to `test(f)` for some equality-preserving function `f`. However, when `To` is a reference type but `FromR` is such that the conversion binds the reference to a temporary, the `static_cast` is valid but `test(f)` would produce a dangling reference.

We should rephrase this requirement to just perform the implicit conversion in words. Using a function handles the convert-`void`-to-`void` case, but the semantic requirements wording already excluded that case anyway.

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3609(i). `std::ranges::iota_view<int, long>` has non-subtractable `iterator` and `sentinel` types

Section: 25.6.4.4 [range.iota.sentinel] Status: New Submitter: Jiang An Opened: 2021-09-25 Last modified: 2021-10-14

Priority: 3

View all issues with New status.

Discussion:

Currently `std::ranges::iota_view<int, long>` uses its special `sentinel` type, as its `W` and `Bound` are different types and `Bound` is not `std::unreachable_sentinel_t`. However, as `W` (`int`) is not an iterator type, the iterator and sentinel types don't satisfy `std::sized_sentinel_for`, and thus not subtractable.

IMO we should handle `operator-` overloads for `iota_view::iterator` and `iota_view::sentinel` like `iota_view::size` and `operator-` for `iota_view::iterator`.

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

[Tim Song commented:]

We might be able to simplify the `(y_value > x.value_)` conditional since we know that we are working with an iterator and its sentinel.

Proposed resolution:

This wording is relative to N4892.

1. Modify 25.6.4.4 [range.iota.sentinel] as indicated:

   > >     namespace std::ranges {
   > >       template<weakly_incrementable W, semiregular Bound>
   > >         requires weakly-equality-comparable-with<W, Bound> && copyable<W>
   > >       struct iota_view<W, Bound>::sentinel {
   > >       private:
   > >         Bound bound_ = Bound(); // exposition only
   > >       public:
   > >         sentinel() = default;
   > >         constexpr explicit sentinel(Bound bound);

   > >         friend constexpr bool operator==(const iterator& x, const sentinel& y);

   > >         friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y)
   > >           requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;

   > >         friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y)
   > >           requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;
   > >       };
   > >     }

   > […]

   >     friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y)
   >       requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;

   > > -3- Effects: Equivalent to: `return x.value_ - y.bound_;`

   > > >     using D = IOTA-DIFF-T(W);
   > > >     if constexpr (is-integer-like<W>) {
   > > >       auto y_value = W(y.bound_);
   > > >       if constexpr (is-signed-integer-like<W>) {
   > > >         return D(D(x.value_) - D(y_value));
   > > >       } else {
   > > >         return (y_value > x.value_)
   > > >           ? D(-D(y_value - x.value_))
   > > >           : D(x.value_ - y_value);
   > > >       }
   > > >     } else {
   > > >       return x.value_ - y.bound_;
   > > >     }

   >     friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y)
   >       requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;

   > > -4- Effects: Equivalent to: `return -(y - x);`

### 3614(i). `iota_view::size` and the most negative signed integer values

Section: 25.6.4.2 [range.iota.view] Status: New Submitter: Jiang An Opened: 2021-10-01 Last modified: 2021-10-14

Priority: 3

View all other issues in [range.iota.view].

View all issues with New status.

Discussion:

According to 25.6.4.2 [range.iota.view]/15, when both `W` and `Bound` are integer-like, the expression in the return statement uses `-value_` and `-bound_`. These operations result in undefined behavior when `-` is applied to the most negative integer value of a promoted type.

I believe that we can simply convert `value_` and `bound_` to the return type (`make-unsigned-like-t<common_type_t<W, Bound>>`) and then perform the subtraction. Such method should give the same results with UB eliminated.

Additionally, if we decide that `iota_view<uint8_t, uint8_t>(uint8_t(1)).size()` is well-defined (LWG 3597(i)), it should give the correct result. We can truncate the result to fit the type `W`.

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4892.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: Just fixes the most negative values

1. Modify 25.6.4.2 [range.iota.view] as indicated:

   >     constexpr auto size() const requires see below;

   > > -15- Effects: Equivalent to:

   > > >     if constexpr (is-integer-like<W> && is-integer-like<Bound>) {
   > > >       return (value_ < 0)
   > > >         ? ((bound_ < 0)
   > > >           ? to-unsigned-like(-value_) - to-unsigned-like(-bound_)
   > > >           : to-unsigned-like(bound_) + to-unsigned-like(-value_))
   > > >         : to-unsigned-like(bound_) - to-unsigned-like(value_);
   > > >       using UC = make-unsigned-like-t<common_type_t<W, Bound>>;
   > > >       return UC(bound_) - UC(value_);
   > > >     } else
   > > >       return to-unsigned-like(bound_ - value_);

   > > -16- Remarks: […]

Option B: Also fixes pathological cases involving unsigned-integer-like types

1. Modify 25.6.4.2 [range.iota.view] as indicated:

   >     constexpr auto size() const requires see below;

   > > -15- Effects: Equivalent to:

   > > >     if constexpr (is-integer-like<W> && is-integer-like<Bound>) {
   > > >       return (value_ < 0)
   > > >         ? ((bound_ < 0)
   > > >           ? to-unsigned-like(-value_) - to-unsigned-like(-bound_)
   > > >           : to-unsigned-like(bound_) + to-unsigned-like(-value_))
   > > >         : to-unsigned-like(bound_) - to-unsigned-like(value_);
   > > >       using UC = make-unsigned-like-t<common_type_t<W, Bound>>;
   > > >       if constexpr (is-signed-integer-like<W>)
   > > >         return UC(bound_) - UC(value_);
   > > >       else
   > > >         return UC(W(UC(bound_) - UC(value_)));
   > > >     } else
   > > >       return to-unsigned-like(bound_ - value_);

   > > -16- Remarks: […]

### 3620(i). What are execution character sets and execution wide-character sets (after P2314R4)?

Section: 16.3.3.3.4.1 [character.seq.general] Status: New Submitter: Dawn Perchik Opened: 2021-10-17 Last modified: 2022-01-29

Priority: 3

View all issues with New status.

Discussion:

The definitions for execution character set and execution wide-character set were reworded and moved to 16.3.3.3.4.1 [character.seq.general]/p1.2 after applying CWG motion 10 "P2314R4 Character sets and encodings", but I can't figure out what these terms mean from the wording, which now reads:

> "The execution character set and the execution wide-character set are supersets of the basic literal character set (5.3 [lex.charset]). The encodings of the execution character sets and the sets of additional elements (if any) are locale-specific."

Would it be possible to provide complete definitions for these and give examples of each?

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll. Should say something, even if just "unspecified". P1885R9 will expose the possible encoding programmatically, so could say it's implementationd-defined and implementations can document that you should ask `std::text_encoding`.

Proposed resolution:

### 3623(i). Uses of `std::reverse_iterator` with containers should not require manually including `<iterator>`

Section: 24.5.1.1 [reverse.iterators.general] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29

Priority: 3

View all issues with New status.

Discussion:

Currently it is unspecified whether the definitions of `std::reverse_iterator` and its related operators are available in `<vector>`, `<array>`, etc. So, it's unspecified now whether the following program is well-formed because it's unspecified whether the equality operator is available:

>     #include <vector>

>     int main()
>     {
>       auto v = std::vector<int>(42);
>       for (auto it = v.rbegin(); it != v.rend(); ++it);
>       for (auto it = std::rbegin(v); it != std::rend(v); ++it);
>     }

Such underspecification also leaves the guarantee that `std::rbegin`, `std::rend`, `std::crbegin`, and `std::crend` are available in some other headers seems not so meaningful. In order to guarantee these function templates can be used meaningfully with containers, users are still required to include `<iterator>` manually.

I think the standard should guarantee that wherever the member `rbegin` (that returns `std::reverse_iterator`) or `std::rbegin` is provided, the definitions of `std::reverse_iterator` and its related operators are also provided. This strategy is already implemented by libc++, libstdc++, and MSVC STL, and thus I believe we should standardize it to reduce uncertainty for users.

Note that the situation for `std::reverse_iterator` is different from LWG 1361(i), because every operation on `std::size_t` is still valid when the typedef-name itself is absent, but `==` and `!=` on `std::reverse_iterator` fail if the corresponding declarations are unavailable.

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4901.

1. Modify 24.5.1.1 [reverse.iterators.general] as indicated:

   > -1- Class template `reverse_iterator` is an iterator adaptor that iterates from the end of the sequence defined by its underlying iterator to the beginning of that sequence.

   > -?- In addition to being available via inclusion of the `<iterator>` header, class template `reverse_iterator` and function templates in 24.5.1.8 [reverse.iter.cmp] and 24.5.1.9 [reverse.iter.nonmember] are available when any of the following headers are included: `<array>` (23.3.2 [array.syn]), `<deque>` (23.3.4 [deque.syn]), `<forward_list>` (23.3.6 [forward.list.syn]), `<list>` (23.3.10 [list.syn]), `<map>` (23.4.2 [associative.map.syn]), `<regex>` (28.6.3 [re.syn]), `<set>` (23.4.5 [associative.set.syn]), `<span>` (23.7.2.1 [span.syn]), `<stacktrace>` (19.6.2 [stacktrace.syn]), `<string>` (27.4.2 [string.syn]), `<string_view>` (27.3.2 [string.view.synop]), `<unordered_map>` (23.5.2 [unord.map.syn]), `<unordered_set>` (23.5.5 [unord.set.syn]), and `<vector>` (23.3.12 [vector.syn]).

### 3626(i). Is `std::basic_stacktrace` required to use contiguous storage?

Section: 19.6.4.1 [stacktrace.basic.overview] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29

Priority: 3

View all issues with New status.

Discussion:

Currently `std::basic_stacktrace` has an exposition-only `std::vector` member for storing its elements. According to 16.3.3.6 [objects.within.classes]/3, it seems that it is effectively required that elements of a `std::basic_stacktrace` are contiguously stored. However, the implication seems not used otherwhere. The iterator type of a `std::basic_stacktrace` is only required to be random access iterator.

IMO if it is required that `std::basic_stacktrace` uses contiguous storage, we should explicitly strengthen some requirements, perhaps a the member function data should be added.

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll.
"The problem here is that a handful of member functions (`operator[]`, `at`, perhaps `begin`) expose references to the vector elements directly, which can be read to require contiguity. We should rephrase the members at issue to not do that."

Proposed resolution:

This wording is relative to N4901.

> [Drafting note: The proposed wording below contains also conditional changes, it is therefore depending upon a decision]

1. Modify 19.6.4.1 [stacktrace.basic.overview] as indicated:

   > -1- The class template `basic_stacktrace` satisfies the requirements of an allocator-aware container (Table 80 [tab:container.alloc.req]), a sequence container (23.2.4 [sequence.reqmts]), a contiguous container, and a reversible container (23.2.2 [container.requirements.general]) except that […]

2. Modify 19.6.4.3 [stacktrace.basic.obs] as indicated:

   >     using const_iterator = implementation-defined;

   > > -1- The type models `random_access_iteratorcontiguous_iterator` (24.3.4.13 [iterator.concept.random.access]24.3.4.14 [iterator.concept.contiguous]) and meets the Cpp17RandomAccessIterator requirements (24.3.5.7 [random.access.iterators]).

Optional additional changes (the following parts are proposed only if `data()` is wanted)

3. Modify 19.6.4.1 [stacktrace.basic.overview], class template `basic_stacktrace` synopsis, as indicated:

   >     […]
   >     const_reference operator[](size_type) const;
   >     const_reference at(size_type) const;

   >     const stacktrace_entry* data() const noexcept;

   >     // 19.6.4.4 [stacktrace.basic.cmp], comparisons
   >     […]

4. Modify 19.6.4.3 [stacktrace.basic.obs] as indicated:

   >     const_reference at(size_type frame_no) const;

   > > -13- […]

   > > -14- […]

   >     const stacktrace_entry* data() const noexcept;

   > > -?- Returns: `frames_.data()`.

### 3628(i). "Effects: Equivalent to:" and uninitialized memory algorithms

Section: 26.11 [specialized.algorithms] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29

Priority: 3

View other active issues in [specialized.algorithms].

View all other issues in [specialized.algorithms].

View all issues with New status.

Discussion:

Most uninitialized memory algorithms (26.11 [specialized.algorithms]) are specified by plain "Effects: Equivalent to:". According to 16.3.2.4 [structure.specifications]/4, such wording requires propagation of "Constraints" of selected constructors. The first two overloads of `std::reduce` (26.10.4 [reduce]) are specified similarly.

I feel the wording for uninitialized memory algorithms is incorrect, because it means that the constraints, especially for algorithms in the `std`, depend on "`Constraints`" in the specifications of many standard library types (but not any user-defined type), which is implementable but brings serious inconsistency.

Perhaps we should add "Mandates:" to these algorithms (except for algorithms in `std::ranges`).

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll.
Tim: "P2. Not for this particular case (I'm pretty sure there'll be agreement that this shouldn't induce any constraint), but for the more general issue of "Effects: Equivalent to" propagating Constraints:; I'm not sure that's the right approach in general (unlike the other elements, Constraints: requires special handling beyond "use this code" and is pretty hard to work through if we have a lengthy code block) - and it certainly doesn't really make a lot of sense to propagate Constraints: but not actual core-language constraints."

Proposed resolution:

### 3630(i). Inconsistent `basic_regex` construction and assignment from iterator range

Section: 28.6.7.2 [re.regex.construct] Status: New Submitter: Jonathan Wakely Opened: 2021-10-31 Last modified: 2022-01-29

Priority: 4

View other active issues in [re.regex.construct].

View all other issues in [re.regex.construct].

View all issues with New status.

Discussion:

We have:

    template<class ForwardIterator>
      basic_regex(ForwardIterator first, ForwardIterator last,
                  flag_type f = regex_constants::ECMAScript);

and:

    template<class InputIterator>
      basic_regex& assign(InputIterator first, InputIterator last,
                              flag_type f = regex_constants::ECMAScript);

Ignoring the lack of proper requirements (which is LWG 3341(i)), why does the constructor take forward iterators, but the assign function takes input iterators? Why could construction from input iterators not be implemented as simply `assign(first, last, f)`?

The current constructor signature is the result of N2409 which was resolving LWG 682(i). It looks like the `assign` function should have been changed at the same time, to keep them consistent. I see no reason why they can't both take input iterators. The meta-programming needed to avoid an additional string copy for the input iterator case is trivial with `if constexpr` and C++20 iterator concepts.

[2022-01-29; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

### 3633(i). Atomics are copy constructible and copy assignable from `volatile` atomics

Section: 32.5 [atomics] Status: New Submitter: Wesley Maxey Opened: 2021-11-05 Last modified: 2024-01-29

Priority: 3

View all other issues in [atomics].

View all issues with New status.

Discussion:

The specification of `atomic` and `atomic<T*>` (in 32.5.8.1 [atomics.types.generic.general] and 32.5.8.5 [atomics.types.pointer]) explicitly deletes the following functions:

>     atomic(const atomic&) = delete;
>     atomic& operator=(const atomic&) = delete;
>     atomic& operator=(const atomic&) volatile = delete;

The intent is to make atomic objects not copyable, so that initializing an atomic object from another atomic, or assigning an atomic object with a value from another atomic, must be an explicit operation.

We also explicitly support `volatile` objects of types that are specializations of `std::atomic`; some of the functions that are vital for the support of volatile atomics are the following conversion operators:

>     operator T() const volatile noexcept; // for non-pointers
>     operator T*() const volatile noexcept; // for pointers

The presence of this conversion operator means that all the statements in the following piece of code compile successfully today, despite the deleted functions mentioned earlier:

>     volatile std::atomic<int> a;
>     volatile std::atomic<int> b(a);
>     std::atomic<int> c(a);
>     b = a;
>     c = a;

However, if `a` is not a volatile object, none of the last four lines compile.

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll.
This PR would allow

    atomic<int> x, y = std::move(x);

because const volatile& doesn't bind to rvalues. It sounds like we'll need to delete both const volatile& and const volatile&&.

Proposed resolution:

This wording is relative to N4901.

1. Modify 32.5.8.1 [atomics.types.generic.general], class template `atomic` synopsis, as indicated:

   >     […]
   >     atomic(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) volatile = delete;
   >     […]

2. Modify 32.5.8.3 [atomics.types.int], class template `atomic<integral>` specialization synopsis, as indicated:

   >     […]
   >     atomic(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) volatile = delete;
   >     […]

3. Modify 32.5.8.4 [atomics.types.float], class template `atomic<floating-point>` specialization synopsis, as indicated:

   >     […]
   >     atomic(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) volatile = delete;
   >     […]

4. Modify 32.5.8.5 [atomics.types.pointer], class template `atomic<T*>` partial specialization synopsis, as indicated:

   >     […]
   >     atomic(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) = delete;
   >     atomic& operator=(const volatile atomic&) volatile = delete;
   >     […]

### 3634(i). When are static-duration `memory_resource` objects destroyed?

Section: 20.5.4 [mem.res.global], 19.5.3.5 [syserr.errcat.objects] Status: New Submitter: Jiang An Opened: 2021-11-07 Last modified: 2022-01-29

Priority: 3

View all other issues in [mem.res.global].

View all issues with New status.

Discussion:

Both `std::pmr::new_delete_resource` and `std::pmr::null_memory_resource` return pointers to static-duration objects. It seems unspecified when the pointed-to objects are destroyed, so users can't reliably use these objects during destructions of their static-duration objects.

`std::generic_category` and `std::system_category` have the same issue, except that the referred-to objects may have different storage duration.

Should we specify in which case can these objects be used in the termination of a program?

[2022-01-29; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3637(i). `pmr::memory_resource::do_allocate` needs clarification

Section: 20.5.2 [mem.res.class] Status: New Submitter: Jonathan Wakely Opened: 2021-11-12 Last modified: 2022-01-30

Priority: 3

View all other issues in [mem.res.class].

View all issues with New status.

Discussion:

20.5.2.3 [mem.res.private] says that `pmr::memory_resource::do_allocate` returns "a pointer to allocated storage" and references 6.8.6.5.2 [basic.stc.dynamic.allocation]. But it's not really clear which parts of 6.8.6.5.2 [basic.stc.dynamic.allocation] define "allocated storage". `pmr::memory_resource::allocate` is not "an allocation function" and not a "replaceable allocation function", so "the value returned by a replaceable allocation function is a non-null pointer value" doesn't apply here, and neither does "different from any previously returned value".

Is `pmr::memory_resource::allocate` allowed to return a null pointer on success? Is it allowed to return the same address twice, without an intervening deallocation? What about if you call `pmr::monotonic_buffer_resource::release()`, is that a deallocation?

When discussed on the reflector the consensus was that returning null should not be allowed, it should throw an exception or return a valid dereferenceable pointer. The reference to 6.8.6.5.2 [basic.stc.dynamic.allocation] doesn't work to specify this though, so we should restate the requirements without directly using the core wording for `operator new`.

It was also suggested that returning the same value should not be allowed without an intervening deallocation, but that "deallocation" should not only mean a call to deallocate on the resource, but include things like `pmr::monotonic_buffer_resource::release()`, and when a memory resource's destructor returns memory to an upstream resource.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3640(i). Clarify which exceptions are propagated

Section: 16.4.4 [utility.requirements] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2021-11-01 Last modified: 2022-10-01

Priority: 3

View all other issues in [utility.requirements].

View all issues with New status.

Discussion:

This originated from the editorial issues #4863 and #4869.

Some Throws: elements are specified to throw the exceptions an evaluation of `E` exits with. This wording excludes exceptions thrown involving the initialization and destruction of parameters of `E`, temporaries of `E`, and the destruction of the result of `E`.

The proposed wording below fixes this with front matter. As if affects more than just Throws: elements, it talks about requirements and guarantees when `E` exits via an exception.

As noted in the originating editorial issues, some LWG members prefer fixing each individual case of wording used to describe exception propagation rather than patching them up with front matter.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll. "Any throwing destructor is library UB already, so there's no need to contort the wording to accommodate those."

Previous resolution [SUPERSEDED]:

> This wording is relative to N4901.

> 1. Add a new subclause [exception.propagation] at the end of 16.4.4 [utility.requirements] (after 16.4.4.6.2 [allocator.requirements.completeness]):

>    > 16.4.4.? Exception propagation requirements [exception.propagation]

>    > -?- Some functions defined in the C++ standard library impose requirements and guarantees R-G when a described evaluation `E` exits via an exception. Let `F` be an evaluation that is implied by evaluating `E` up to the complete evaluation of its enclosing full-expression. Unless stated otherwise, an execution of `F` that exits via an exception also has R-G imposed. [Note ?: This includes when initializing and destroying parameters, evaluating default arguments, and destroying temporaries (including discarded-value expressions) (7.6.1.3 [expr.call]) exit via an exception. — end note]

[2022-09-28; Johel provides revised wording]

Proposed resolution:

This wording is relative to N4917.

1. Add a new subclause [exception.propagation] at the end of 16.4.4 [utility.requirements] (after 16.4.4.6.2 [allocator.requirements.completeness]):

   > 16.4.4.? Exception propagation requirements [exception.propagation]

   > -?- Some functions defined in the C++ standard library impose requirements and guarantees R-G when a described evaluation `E` of a constructor or construction exits via an exception. Let `F` be the initialization denoted by `E`. Unless stated otherwise, `F` also has R-G imposed. [Note ?: This includes the initialization of parameters and the evaluation of default arguments as part of `F`. — end note]

### 3642(i). `move_only_function` assignment operators seem to be defined suboptimal

Section: 22.10.17.4.3 [func.wrap.move.ctor] Status: New Submitter: Alexander Guteniev Opened: 2021-11-20 Last modified: 2022-01-30

Priority: 3

View other active issues in [func.wrap.move.ctor].

View all other issues in [func.wrap.move.ctor].

View all issues with New status.

Discussion:

22.10.17.4.3 [func.wrap.move.ctor]/22 and 22.10.17.4.3 [func.wrap.move.ctor]/25 define the effects of assignment as following:

>     move_only_function& operator=(move_only_function&& f);

> > Effects: Equivalent to: `move_only_function(std::move(f)).swap(*this);`

> […]

>     template<class F> move_only_function& operator=(F&& f);

> > Effects: Equivalent to: `move_only_function(std::forward<F>(f)).swap(*this);`

The assignment via `swap` with temporary makes the implementation to do the following:

- move out the previous target to a temporary location

- move in the new target

- finally destroy the previous target.

As everything is `noexcept` here, I think it can be short cut to just:

- destroy the previous target.

- move in the new target

Looks like the implementation cannot do such optimization in a generic case with small functor optimization enabled and non-trivial move constructor for the new target and with non-trivial destruction of the previous target, since the difference is observable.

Apparently the optimization is precluded for no reason.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll. Some suggestions for NAD, but others disagreed.

Proposed resolution:

This wording is relative to N4901.

1. Modify 22.10.17.4.3 [func.wrap.move.ctor] as indicated:

   >     move_only_function& operator=(move_only_function&& f);

   > > -22- Effects: Sets the target object of `this` to the target object of `f` before the assignment and leaves `f` in a valid state with an unspecified value.Equivalent to: `move_only_function(std::move(f)).swap(*this);`

   > […]

   >     template<class F> move_only_function& operator=(F&& f);

   > > -25- Effects: Equivalent to: `*this = move_only_function(std::forward<F>(f)).swap(*this);`

### 3644(i). `std::format` does not define "integer presentation type"

Section: 28.5.2.2 [format.string.std] Status: New Submitter: Charlie Barto Opened: 2021-11-23 Last modified: 2022-11-01

Priority: 2

View other active issues in [format.string.std].

View all other issues in [format.string.std].

View all issues with New status.

Discussion:

28.5.2.2 [format.string.std] specifies the behavior of several format specifiers in terms of "integer presentation types"; for example 28.5.2.2 [format.string.std]/4 states:

> "The sign option is only valid for arithmetic types other than `charT` and `bool` or when an integer presentation type is specified".

Unfortunately nowhere does the standard actually define the term "integer presentation type". The closest it comes is in 28.5.2.2 [format.string.std]/19 and [tab:format.type.int], but that explicitly excludes `charT` and `bool`. [tab:format.type.char] and [tab:format.type.bool] then refer to [tab:format.type.int].

I can come up with many interpretations for what could happen when `'c'` is used with `charT` or `bool`, but the following table is what msvc does right now (throws is the same as does not compile after P2216 in all these cases, although not in general for `'c'`):

> Argument type | Specifiers | Throws?
> --- | --- | ---
> `bool` | `#` | Yes
> `bool` | `#c` | No
> `bool` | `:+` | Yes
> `bool` | `+c` | Yes
> `bool` | `^` | No
> `bool` | `^c` | No
> `bool` | `0` | Yes
> `bool` | `0c` | Yes
> `bool` | `c` | No
> `charT` | `#` | Yes
> `charT` | `#c` | Yes
> `charT` | `+` | Yes
> `charT` | `+c` | Yes
> `charT` | `^` | No
> `charT` | `^c` | No
> `charT` | `0` | Yes
> `charT` | `0c` | Yes

As you can see we don't interpret `'c'` as an "integer type specifier", except when explicitly specified for `bool` with `#`. I think this is because for `#` the standard states

> "This option is valid for arithmetic types other than `charT` and `bool` or when an integer presentation type is specified, and not otherwise",

and [tab:format.type.bool] puts `'c'` in the same category as all the other "integer type specifiers", whereas [tab:format.type.char] separates it out into the char-specific types. If this issue's proposed resolution is adopted our behavior would become non-conforming (arguably it already is) and `"#c"` with `bool`s would become invalid.

[2021-11-29; Tim comments]

This issue touches the same wording area as LWG 3586(i) does.

[2022-01-30; Reflector poll]

Set priority to 2 after reflector poll.

[2021-11-29; Jonathan comments]

LWG 3648(i) removed 'c' as a valid presentation type for `bool`. The last change in the resolution below (and the drafting note) can be dropped.

LWG 3586(i) could be resolved as part of this issue by using "this is the default unless formatting a floating-point type or using an integer presentation type" for '<' and by using "this is the default when formatting a floating-point type or using an integer presentation type" for '>'.

Proposed resolution:

This wording is relative to N4901.

1. Modify 28.5.2.2 [format.string.std] as indicated:

   > -6- The `#` option causes the alternate form to be used for the conversion. This option is only valid for arithmetic types other than `charT` and `bool` or when an integer presentation type is specified, and not otherwise. For integral types, the alternate form inserts the base prefix (if any) specified in Table 65 into the output after the sign character (possibly space) if there is one, or before the output of `to_chars` otherwise. For floating-point types, the alternate form causes the result of the conversion of finite values to always contain a decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result of these conversions only if a digit follows it. In addition, for `g` and `G` conversions, trailing zeros are not removed from the result.

   > […]

   > > [Drafting note: This modification is a simple cleanup given the other changes further below, to bring the wording for `#` in line with the wording for the other modifiers, in the interest of preventing confusion.]

   > […]

   > -16- The type determines how the data should be presented.

   > -?- An integer presentation type is one of the following type specifiers in Table [tab:format.type.integer_presentation], or none, if none is defined to have the same behavior as one of the type specifiers in Table [tab:format.type.integer_presentation].

   > > Table ? — Meaning of type options for integer representations [tab:format.type.integer_presentation]
   > > Type | Meaning
   > > --- | ---
   > > `b` | `to_chars(first, last, value, 2);` the base prefix is `0b`.
   > > `B` | The same as `b`, except that the base prefix is `0B`.
   > > `d` | `to_chars(first, last, value)`.
   > > `o` | `to_chars(first, last, value, 8)`; the base prefix is `0` if `value` is nonzero and is empty otherwise.
   > > `x` | `to_chars(first, last, value, 16)`; the base prefix is `0x`.
   > > `X` | The same as `x`, except that it uses uppercase letters for digits above `9` and the base prefix is `0X`.

   > > [Drafting note: This is the same as [tab:format.type.int] with "none" and `'c'` removed]

   > -17- The available string presentation types are specified in Table 64 ([tab:format.type.string]).

   > […]

   > > Table 65 — Meaning of type options for integer types [tab:format.type.int]
   > > Type | Meaning
   > > --- | ---
   > > `b`, `B`, `d`, `o`, `x`, `X` | As specified in Table [tab:format.type.integer_presentation]`to_chars(first, last, value, 2);` the base prefix is `0b`.
   > > `B` | The same as `b`, except that the base prefix is `0B`.
   > > `c` | Copies the character `static_cast<charT>(value)` to the output. Throws `format_error` if `value` is not in the range of representable values for `charT`.
   > > `d` | `to_chars(first, last, value)`.
   > > `o` | `to_chars(first, last, value, 8)`; the base prefix is `0` if `value` is nonzero and is empty otherwise.
   > > `x` | `to_chars(first, last, value, 16)`; the base prefix is `0x`.
   > > `X` | The same as `x`, except that it uses uppercase letters for digits above `9` and the base prefix is `0X`.
   > > none | The same as `d`. [Note 8: If the formatting argument type is `charT` or `bool`, the default is instead `c` or `s`, respectively. — end note]

   > > Table 66 — Meaning of type options for `charT` [tab:format.type.char]
   > > Type | Meaning
   > > --- | ---
   > > none, `c` | Copies the character to the output.
   > > `b`, `B`, `d`, `o`, `x`, `X` | As specified in Table [tab:format.type.int][tab:format.type.integer_presentation].

   > > Table 67 — Meaning of type options for `bool` [tab:format.type.bool]
   > > Type | Meaning
   > > --- | ---
   > > none, `s` | Copies textual representation, either `true` or `false`, to the output.
   > > `b`, `B`, `c`, `d`, `o`, `x`, `X` | As specified in Table [tab:format.type.int][tab:format.type.integer_presentation] for the value `static_cast<unsigned char>(value)`.
   > > `c` | Copies the character `static_cast<unsigned char>(value)` to the output.

   > > [Drafting note: allowing the `'c'` specifier for `bool` is pretty bizarre behavior, but that's very clearly what the standard says now, so I'm preserving it. I would suggest keeping discussion of changing that behavior to a separate issue or defect report (the reworking of the tables in this issue makes addressing that easier anyway).

   > > The inconsistency with respect to using `static_cast<unsigned char>` here and `static_cast<charT>` in [tab:format.type.int] is pre-existing and should be addressed in a separate issue if needed. ]

### 3647(i). `nothrow-input-iterator` constraints should not mention copying

Section: 26.11.2 [special.mem.concepts] Status: Ready Submitter: Konstantin Varlamov Opened: 2021-11-30 Last modified: 2026-06-15

Priority: 3

View all issues with Ready status.

Discussion:

26.11.2 [special.mem.concepts] states that a type qualifies as a `nothrow-input-iterator` only if no exceptions are thrown from, among other things, copy construction and copy assignment. However, being copyable isn't part of the requirements for an `input_iterator` on which `nothrow-input-iterator` is based (indeed, one of the things `forward_iterator` adds to `input_iterator` is copyability), and the `nothrow-input-iterator` concept doesn't add any new constraints related to copyability.

[2021-12-19; Daniel comments]

During LWG discussion of the issue one argument brought forward against the proposed wording was that it might be incomplete, because it doesn't adjust the `nothrow-forward-iterator` concept, which adds among other things the `copyable` requirements. But `nothrow-forward-iterator` also requires `nothrow-sentinel-for<I, I>`, which already extends this necessary no-throw requirement for copy operations by p. 4:

> Types `S` and `I` model `nothrow-sentinel-for` only if no exceptions are thrown from copy construction, move construction, copy assignment, move assignment, or comparisons between valid values of type `I` and `S`.

It should also be emphasized that the definitions of move construction (3.34 [defns.move.constr]) and move assignment (3.33 [defns.move.assign]) are compatible even for copyable input iterator types, because these definitions refer just to expression conditions, and not to concrete operator overloads. So as long as an implementation applies these expression conditions, we are safe.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll.

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N4901.

1. Modify 26.11.2 [special.mem.concepts] as indicated:

   >     template<class I>
   >     concept nothrow-input-iterator = // exposition only
   >       input_iterator<I> &&
   >       is_lvalue_reference_v<iter_reference_t<I>> &&
   >       same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>>;

   > > -2- A type `I` models `nothrow-input-iterator` only if no exceptions are thrown from increment, copy construction, move construction, copy assignment, move assignment, or indirection through valid iterators.

### 3651(i). Unspecified lifetime guarantees for the format string

Section: 28.5 [format] Status: New Submitter: Barry Revzin Opened: 2021-12-08 Last modified: 2022-01-30

Priority: 3

View other active issues in [format].

View all other issues in [format].

View all issues with New status.

Discussion:

Is this program guaranteed to be valid:

>     #include <format>
>     #include <algorithm>

>     struct Thing { };

>     template <>
>     struct std::formatter<Thing> {
>       std::string_view spec;

>       constexpr auto parse(std::format_parse_context& ctx) {
>         auto end = std::find(ctx.begin(), ctx.end(), '}');
>         spec = std::string_view(ctx.begin(), end);
>         return end;
>       }

>       auto format(Thing, std::format_context& ctx) {
>         return std::ranges::copy(spec, ctx.out()).out;
>       }
>     };

>     int main() {
>       std::print("{:lwg issue}\n",  Thing{});
>     }

In `parse()`, the formatter for `Thing` holds onto a string view of its specifiers. And then in `format()`, it just prints them. I don't think we say anywhere that this works. Does this code print `"lwg issue"` because there's no issue or does it print some garbage memory somewhere because there is one?

libfmt's implementation internally stores `string_view`'s into the format string (for named argument support), which implies that it should work. But it'd be nice to come out and say that.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll.
"Presumably we need to say in [formatter.requirements] that [`pc.begin()`, `pc.end()`) is guaranteed to be a valid range until the next call to `parse()` or `f` is destroyed, whichever comes first."

Proposed resolution:

### 3658(i). `basic_streambuf::sputn` is both overspecified and underspecified

Section: 31.6.3.3.5 [streambuf.pub.put] Status: New Submitter: Jonathan Wakely Opened: 2022-01-17 Last modified: 2022-01-30

Priority: 3

View all issues with New status.

Discussion:

The specification for `basic_streambuf::sputn` is:

> Returns: `xsputn(s, n)`.

One interpretation of this implies that `sputn` can't insert characters directly into the put area if there is space for them, it has to make a virtual call. That has significant overhead for repeated calls, such as inserting many small strings/string_views in a loop.

But another interpretation is that it doesn't use "Effects: Equivalent to" or anything like that, so doesn't say an actual call to `xsputn` happens. Strictly speaking, it only says it returns the value that `xsputn` would return, and doesn't even have to produce any of its effects!

We should describe the effects, not the return value, and we should do so in a way that does not overconstrain the implementation. It should not be necessary to make a virtual call to `xsputn` if the put area has capacity for the characters. On the other hand, if it doesn't have capacity, then calling `xsputn` is the best option; it allows the derived `streambuf` to decide how best to handle large writes.

The proposed resolution replaces the Returns: element with an Effects: element, so that we specify that those effects actually occur. A normative remark is added to give the implementation leeway to avoid the virtual call when it isn't needed.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll. Jonathan to revise P/R to also cover `sgetn`.

Proposed resolution:

This wording is relative to N4901.

1. Modify 31.6.3.3.5 [streambuf.pub.put] as indicated:

   >     streamsize sputn(const char_type* s, streamsize n);

   > > -?- Preconditions: `[s, s+n)` is a valid range.

   > > -?- Effects: `return xsputn(s, n)`.

   > > -?- Remarks: When `(epptr() - pptr()) >= n`, it is unspecified whether the characters are written directly to the output sequence without calling `xsputn`.

   > > -2- Returns: `xsputn(s, n)`.

### 3663(i). `basic_string(const T&, const Alloc&)` turns moves into copies

Section: 27.4.3.3 [string.cons], 27.4.3.7.3 [string.assign] Status: New Submitter: Jonathan Wakely Opened: 2022-01-21 Last modified: 2022-01-30

Priority: 3

View all other issues in [string.cons].

View all issues with New status.

Discussion:

This will do a copy not a move:

>     struct Str : std::string {
>       Str() = default;
>       Str(Str&& s) : std::string(std::move(s)) { }
>     };
>     Str s;
>     Str s2(std::move(s));

The problem is that the new C++17 constructor:

>     basic_string(const T&, const Alloc& = Alloc());

is an exact match, but the `basic_string` move constructor requires a derived-to-base conversion.

This is a regression since C++14, because the move constructor was called in C++14.

This problem also exists for `assign(const T&)` and `operator=(const T&)`.

We can fix this by constraining those functions with `!derived_from<T, basic_string>`, so that the move constructor is the only viable function. Libstdc++ has done something very similar since 2017, but using `!is_convertible<const T*, const basic_string*>` instead of `derived_from`.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll. Jonathan to revise P/R to use `is_base_of` or `is_convertible` instead of `derived_from`.

Proposed resolution:

This wording is relative to N4901.

1. Modify 27.4.3.3 [string.cons] as indicated:

   >     template<class T>
   >       constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());

   > > -7- Constraints:

   > > 1. (7.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (7.?) — `derived_from<T, basic_string>` is `false` and

   > > 3. (7.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -8- Effects: Creates a variable, `sv`, as if by `basic_string_view<charT, traits> sv = t;` and then behaves the same as `basic_string(sv.data(), sv.size(), a)`.

   > […]

   >     template<class T>
   >       constexpr basic_string& operator=(const T& t);

   > > -27- Constraints:

   > > 1. (27.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (27.?) — `derived_from<T, basic_string>` is `false` and

   > > 3. (27.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -28- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return assign(sv);

2. Modify 27.4.3.7.3 [string.assign] as indicated:

   >     template<class T>
   >       constexpr basic_string& assign(const T& t);

   > > -4- Constraints:

   > > 1. (4.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (4.?) — `derived_from<T, basic_string>` is `false` and

   > > 3. (4.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -5- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return assign(sv.data(), sv.size());

### 3665(i). Is `std::allocator_traits<Alloc>::rebind_alloc` SFINAE-friendly?

Section: 20.2.9.2 [allocator.traits.types] Status: New Submitter: Jiang An Opened: 2022-01-24 Last modified: 2022-03-04

Priority: 3

View all other issues in [allocator.traits.types].

View all issues with New status.

Discussion:

20.2.9.2 [allocator.traits.types]/11 says that the instantiation of `rebind_alloc` is sometimes ill-formed, however, it's unclear such ill-formedness results in substitution failure or hard error. It seems that current implementations (libc++, libstd++, MSVC STL) give substitution errors, and we should reword 20.2.9.2 [allocator.traits.types]/11 with "Constraints:".

[2022-01-29; Daniel comments]

This issue has some overlap with LWG 3545(i) in regard to the question how we should handle the `rebind` member template of the `pointer_traits` primary template as specified by 20.2.3.2 [pointer.traits.types] p3. It would seem preferable to decide for the same approach in both cases.

[2022-03-04; Reflector poll]

Set priority to 3 after reflector poll. Probably NAD, since [allocator.requirements.general]/11 allows any allocator instantiation to fail with a hard error outside the immediate context, making it impossible to guarantee a SFINAE-friendly result. Also unclear what motivation there is for it being SFINAE friendly.

Proposed resolution:

### 3666(i). `join_view`'s difference type is too small

Section: 25.7.14 [range.join] Status: New Submitter: Tomasz Kamiński Opened: 2022-01-30 Last modified: 2022-03-04

Priority: 2

View all other issues in [range.join].

View all issues with New status.

Discussion:

Despite the fact that `join_view` may produce more elements than each joined view(s) contains, we require implementations to use the `common_type` of their difference types (per 25.7.14.3 [range.join.iterator] p3), which may lead to UB. As we already have provided implementation freedom to define extended integer-like types we should make provision for them to be used here as well. The same issue applies to the `join_with_view` as proposed by P2441.

[2022-03-04; Reflector poll]

Set priority to 2 after reflector poll. Might be a design issue.

Proposed resolution:

### 3668(i). `[recursive_]directory_iterator` constructors refer to undefined `options`

Section: 31.12.11.2 [fs.dir.itr.members], 31.12.12.2 [fs.rec.dir.itr.members] Status: New Submitter: Jonathan Wakely Opened: 2022-01-31 Last modified: 2024-01-29

Priority: 3

View all other issues in [fs.dir.itr.members].

View all issues with New status.

Discussion:

31.12.11.2 [fs.dir.itr.members] p2 and 31.12.12.2 [fs.rec.dir.itr.members] p3 refer to the `options` parameter, but not all overloads have a parameter of that name.

We need some "for the overloads with …" wording.

[2022-03-04; Reflector poll]

Set priority to 3 after reflector poll.

[2022-12-18; Daniel comments and provides wording]

It seems to me that we should differentiate the problems of `directory_iterator` and `recursive_directory_iterator` regarding the constructors that don't provide any `options` arguments at least from a logical point of view:

From the existing Postconditions: element of `recursive_directory_iterator` we can at least guess that the intention of the existing wording is that these suppose to reflect that the effective options within the Effects: element are also equal to `directory_options::none`. But for `directory_iterator` we don't have any options "getter" nor Postconditions from deducing the intent.

Fortunately, existing implementations (I looked at the current V2 2022 library and libstdc++ implementation) seem to behave already as if `directory_options::none` is set for the affected constructors for both iterator types.

Note that this issue here has some wording overlap with the the proposed wording of LWG 2708(i), I'm therefore using the exact wording form currently suggested there. If LWG would like to change this form (e.g. by using the word "overload" instead of "signature") this should also be done in that other issue to reduce possible merge conflicts. Note that the here suggested form using "signature" has existing practice in other places of Clause 31.12 [filesystems], so I recommend keeping that form.

Proposed resolution:

This wording is relative to N4917.

1. Modify 31.12.11.2 [fs.dir.itr.members] as indicated:

   > [Drafting note:
   > (1) The proposed wording form takes care to use the same wording form used in LWG 2708(i) for `recursive_directory_iterator` at the time of writing.
   > (2) We don't need similar wording for the default constructor because this creates an end iterator and there exist no way for the user to observe the effects of `directory_options` for such an iterator value. ]

   >     directory_iterator() noexcept;

   > > -1- Effects: Constructs the end iterator.

   >     explicit directory_iterator(const path& p);
   >     directory_iterator(const path& p, directory_options options);
   >     directory_iterator(const path& p, error_code& ec);
   >     directory_iterator(const path& p, directory_options options, error_code& ec);

   > > -?- For the signatures with no parameter `options`, let `options` be `directory_options::none`.

   > > -2- Effects: For the directory that `p` resolves to, constructs an iterator for the first element in a sequence of `directory_entry` elements representing the files in the directory, if any; otherwise the end iterator. However, if

   > > >     (options & directory_options::skip_permission_denied) != directory_options::none

   > > and construction encounters an error indicating that permission to access `p` is denied, constructs the end iterator and does not report an error.

   > > […]

2. Modify 31.12.12.2 [fs.rec.dir.itr.members] as indicated:

   > [Drafting note:
   > (1) The proposed wording form takes care to be in sync with the wording form used in LWG 2708(i) at the time of writing. This issue here doesn't touch the wording for the `options` function though, and leaves any changes to LWG 2708(i), because these changes don't seem essential to solve the issue here.
   > (2) We don't need similar wording for the default constructor because this creates an end iterator and from 31.12.12.1 [fs.class.rec.dir.itr.general] p2 we can conclude that user code has no way to determine the effective `directory_options` value of this iterator value (interestingly p15 quoted below can be read to apply here as well, but 31.12.12.1 [fs.class.rec.dir.itr.general] p2 seems to be very prohibitive and LWG 2708(i) is going to solve this special case as well).
   > (3) We easily simplify the wording of p3 as shown below, because the "scope" of the introductory new paragraph includes the Postconditions: element. But similar to the arguments provided in (1) we don't wish to introduce merge conflicts with the LWG 2708(i) wording and therefore intentionally leave any changes of p3 to that issue. ]

   >     recursive_directory_iterator() noexcept;

   > > -1- Effects: Constructs the end iterator.

   >     explicit recursive_directory_iterator(const path& p);
   >     recursive_directory_iterator(const path& p, directory_options options);
   >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec);
   >     recursive_directory_iterator(const path& p, error_code& ec);

   > > -?- For the signatures with no parameter `options`, let `options` be `directory_options::none`.

   > > -2- Effects: Constructs an iterator representing the first entry in the directory to which `p` resolves, if any; otherwise, the end iterator. However, if

   > > >     (options & directory_options::skip_permission_denied) != directory_options::none

   > > and construction encounters an error indicating that permission to access `p` is denied, constructs the end iterator and does not report an error.

   > > -3- Postconditions: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

   > > […]

   > […]

   >     directory_options options() const;

   > > -15- Returns: The value of the argument passed to the constructor for the `options` parameter, if present, otherwise `directory_options::none`.

   > > -16- Throws: Nothing.

### 3669(i). `std::filesystem` operations should be observable behaviour

Section: 4.1.2 [intro.abstract] Status: New Submitter: Jens Maurer Opened: 2022-02-03 Last modified: 2026-06-15

Priority: 3

View all issues with New status.

Discussion:

4.1.2 [intro.abstract] p6.2 should be amended to say that `filesystem` operations such as `mkdir` are observable behaviour. Any resolution would need CWG review.

[2022-03-04; Reflector poll]

Set priority to 3 after reflector poll.

[Brno 2026-06-12; LWG discussion]

Cross reference is now p8.2. Add a bullet with the words from 31.12.13.1 [fs.op.funcs.general].

Proposed resolution:

### 3674(i). Removal of requirement for locale names for construction of locales not explained

Section: 28.3.3.1.4 [locale.members] Status: New Submitter: Hubert Tong Opened: 2022-02-12 Last modified: 2022-03-04

Priority: 2

View all other issues in [locale.members].

View all issues with New status.

Discussion:

LWG 2394(i) removed the only text in the wording that requires that the name of a locale is usable for constructing further locales.

The relevant notes from the wiki appear to make it seem like LWG thought the change was editorial.

Perhaps the resolution was motivated by a different defect than the one that led to the change?

Namely,

>     explicit locale(const char* std_name);

is said to use "standard C locale names".

There is no `LC_MESSAGES` in standard C.

Thus, it is a question whether the aforementioned constructor should be able to consume names produced by the C++ implementation.

See also the use of the name of the C++ locale in `locale::global()` for use with `setlocale`.

The following would restore the equivalence of locales that have the same name. It also addresses the suitability of the name for use with `setlocale` and the `locale(const char*)` constructor as a matter of QoI.

[2022-03-04; Reflector poll]

Set priority to 2 after reflector poll.

Proposed resolution:

This wording is relative to N4901.

1. Modify 28.3.3.1.4 [locale.members] as indicated:

   >     string name() const;

   > > -5- Returns: The name of `*this`, if it has one; otherwise, the string `"*"`.

   > > -?- Remarks: Two locales have identical names only if their facets have identical virtual function semantics.

   > > -?- Recommended practice: The name of a locale that has a name should be such that `setlocale(LC_ALL, name().c_str())` returns a non-null pointer. [Note 1: With such a name `locale(name().c_str())` succeeds and does not throw `runtime_error`. — end note]

### 3675(i). `std::ios_base::iword/pword` might be misspecified

Section: 31.5.2.6 [ios.base.storage] Status: New Submitter: Jiang An Opened: 2022-02-14 Last modified: 2022-03-04

Priority: 4

View all other issues in [ios.base.storage].

View all issues with New status.

Discussion:

Currently 31.5.2.6 [ios.base.storage] p5 and p8 say "On failure, a valid `long&/void*&` initialized to `0`". Such wording seems wrong, because a `long&/void*&` variable or return value can't be initialized with `0`. And the values of referenced objects may be underspecified, because an implementation may reuse the same `long/void*` objects on failure, and thus it's insufficient to specify the initial values of these objects only.

[2022-03-04; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

This wording is relative to N4901.

1. Modify 31.5.2.6 [ios.base.storage] as indicated:

   >     long& iword(int idx);

   > > -3- […]

   > > -4- […]

   > > -5- Returns: On success `iarray[idx]`. On failure, an valid lvalue of type `long&` with value `0L` initialized to `0`.

   >     void*& pword(int idx);

   > > -6- […]

   > > -7- […]

   > > -8- Returns: On success `parray[idx]`. On failure, an valid lvalue of type `void*&` with a null pointer valueinitialized to `0`.

   > > -9- Remarks: After a subsequent call to `pword(int)` for the same object, the earlier return value may no longer be valid.

### 3678(i). Constructors of `std::chrono::time_zone` might be overly unspecified

Section: 30.11.5.1 [time.zone.overview] Status: New Submitter: Jiang An Opened: 2022-02-23 Last modified: 2025-03-19

Priority: 4

View all issues with New status.

Discussion:

In 30.11.5.1 [time.zone.overview], only defaulted move constructor and move assignment operator for `std::chrono::time_zone` are shown, other constructors are said to be "unspecified additional constructors". Presumably the intent is that the default constructor is not declared (suppressed) and the copy constructor is implicitly deleted, but it is not clear if they are not "unspecified additional constructors" and hence implicitly specified.

On the other hand, the defaulted definitions of move functions bring almost no specification, as no exposition only member is shown. So it is unspecified whether these functions are deleted, trivial, `constexpr`, or `noexcept`. Perhaps we want these functions to be non-deleted and `noexcept`, while triviality and `constexpr`-ness should be left unspecified.

[2022-03-04; Reflector poll]

Set priority to 4 after reflector poll.

[2025-03-18; Jonathan provides wording]

I don't think it matters whether they are trivial or constexpr, because they cannot be used. Users only have access to `const time_zone` lvalues via `locate_zone` and the `tzdb::zones` container. The move constructor and move assignment operator only need to exist so that the implementation can populate that container.

Proposed resolution:

This wording is relative to N5001.

1. Modify 30.11.5.1 [time.zone.overview] as indicated:

   >     namespace std::chrono {
   >       class time_zone {
   >         time_zone(unspecified);
   >       public:
   >         time_zone(time_zone&&) = default;
   >         time_zone& operator=(time_zone&&) = default;

   >         // unspecified additional constructors

   >         [...]
   >       };
   >     }

   -1- A `time_zone` represents all time zone transitions for a specific geographic area. `time_zone` construction is unspecified, and performed only as part of database initialization.

   [Note 1: `const time_zone` objects can be accessed via functions such as `locate_zone`. — end note]

### 3679(i). Is `<ranges>` sufficient for `istream_view`?

Section: 25.6.6 [range.istream] Status: LEWG Submitter: Barry Revzin Opened: 2022-02-24 Last modified: 2022-11-10

Priority: 3

View all issues with LEWG status.

Discussion:

The following is rejected by libstdc++:

>     #include <ranges>

>     void call(std::ranges::istream_view<int>& v);

The error is quite cryptic, but ultimately the issue is not including `<istream>`. I think this currently isn't required to work, so the fact that it does not work is conforming. But should it be required to work?

I think either this should work or we should add a note to 25.6.6 [range.istream] about the include.

[2022-03-04; Reflector poll]

Set priority to 3 after reflector poll.

[Kona 2022-11-08; discussed at joint LWG/SG9 session. Send to LEWG (with suggested options)]

Proposed resolution:

### 3680(i). Constructor of `move_only_function` with empty `ref`-qualifier is over-constrained

Section: 22.10.17.4.3 [func.wrap.move.ctor] Status: New Submitter: Zhihao Yuan Opened: 2022-02-27 Last modified: 2022-03-04

Priority: 2

View other active issues in [func.wrap.move.ctor].

View all other issues in [func.wrap.move.ctor].

View all issues with New status.

Discussion:

The following test compiles and holds:

>     struct X
>     {
>       int operator()() & { return 1; }  // #1
>       int operator()() && { return 2; } // #2
>     };

>     using T = move_only_function<int()>;
>     assert(T(X{})() == 1);

In other words, `#2` is never used. But if you really remove `#2`, the code doesn't compile.

The change was introduced between P0288R5 and P0288R6, with an intention (assumed) to require `move_only_function<R(Args...) cv>` erasing callable objects with `operator()` without `ref`-qualifiers. But the actual outcome outlawed programs that were valid when using `std::function`.

It also outlaws future programs such as

>     T x = [captures](this auto&) { return ...; }

where declaring `this auto&&` without forwarding only to satisfy `move_only_function`'s constraints would be strange.

[2022-03-04; Reflector poll]

Set priority to 2 after reflector poll. Probably needs a paper for LEWG.

Proposed resolution:

This wording is relative to N4901.

1. Modify 22.10.17.4.3 [func.wrap.move.ctor] as indicated:

   >     template<class VT>
   >       static constexpr bool is-callable-from = see below;

   > > -1- If `noex` is `true`, `is-callable-from<VT>` is equal to:

   > > >     is_nothrow_invocable_r_v<R, VT cv ref, ArgTypes...> &&
   > > >     is_nothrow_invocable_r_v<R, VT inv-quals, ArgTypes...>

   > > Otherwise, `is-callable-from<VT>` is equal to:

   > > >     is_invocable_r_v<R, VT cv ref, ArgTypes...> &&
   > > >     is_invocable_r_v<R, VT inv-quals, ArgTypes...>

### 3681(i). Further considerations on LWG 3679 (using `pmr::vector` without including `<memory_resource>`)

Section: 20.5.1 [mem.res.syn], 20.2.2 [memory.syn] Status: New Submitter: Jiang An Opened: 2022-02-28 Last modified: 2025-10-22

Priority: 4

View all issues with New status.

Discussion:

The issue reflected in LWG 3679(i) is not limited to `<ranges>` and `std::ranges::istream_view`.

Example:

>      #include <vector>
>     // some standard headers other than <memory> and <memory_resource>

>     template<class T> my_ator {/*definition, meeting the requirements of Cpp17Allocator*/};

>     int main()
>     {
>       std::vector<int> v1; // Generally works. Is this guaranteed?
>       std::pmr::vector<int> v2; // Usually fails to work on libstdc++. Is this intendedly permitted??
>       std::vector<int, my_ator<int>> v3; // Generally works. Is this guaranteed?
>     }

Currently libstdc++ only provides forward declarations of `std::pmr::polymorphic_allocator` in headers of standard allocator-aware containers, which means that users are required to include both `<memory_resource>` and `<vector>` in order to create a `std::pmr::vector<T>` object. If libstdc++ is technically conforming here, one may say the definition of `std::allocator` is also not guaranteed to be available in these headers, so `<memory>` is required to be included together with such a header to make `standard-container<T>` work.

Furthermore, the specification of allocator-aware containers are heavily dependent on `std::allocator_traits`. If these containers are not guaranteed to work when the definition of `std::allocator_traits` is not available, and the definition of `std::allocator_traits` is not guaranteed to be provided in headers of these containers, then users are effectively always required to include `<memory>` in order to create a container object, even if they are using their own allocators.

[2022-05-17; Reflector poll]

Set priority to 4 after reflector poll.

[2025-10-22; Jonathan comments after reflector discussion.]

"We could use the 'In addition to being available via inclusion of ...' form, see also LWG 3623(i)."

Proposed resolution:

### 3682(i). A Cpp17Allocator type can't silently ignore an unsupported alignment

Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2022-03-18 Last modified: 2022-05-17

Priority: 3

View other active issues in [allocator.requirements.general].

View all other issues in [allocator.requirements.general].

View all issues with New status.

Discussion:

Currently (at least since P0593R6), the `allocate` function of an allocator is required to create an array of elements (16.4.4.6.1 [allocator.requirements.general]), which means the allocated storage must be properly aligned (otherwise the array of requested size can't be created). However, according to paragraph 12 it is also allowed that "the allocator also may silently ignore the requested alignment".

IMO the allowance is contradictory and should be removed.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

"You can allocate from such an allocator but you can't construct objects in the allocated memory without manually checking the alignment of the returned pointer. That doesn't seem useful in practice."

"maybe even NAD/LEWG? I can't see us declaring most in-the-wild allocators to no longer be allocators. If anything, a design change is necessary to have a protocol for allocators to declare proper support for overalignment and for `allocator_traits` to implement such support "manually" for older allocators."

Proposed resolution:

### 3684(i). `std::allocator<T>::allocate_at_least` in constant evaluation

Section: 20.2.10.2 [allocator.members] Status: New Submitter: Jiang An Opened: 2022-03-22 Last modified: 2022-05-17

Priority: 3

View all other issues in [allocator.members].

View all issues with New status.

Discussion:

`std::allocator<T>::allocate_at_least` is a constexpr function that allocates memory during constant evaluation, but its restrictions is not clear. Presumably the restrictions are same as those of `std::allocator<T>::allocate`, and we should specify `allocate_at_least` in term of `allocate`.

The MSVC STL implementation returns `allocation_result<T*>{allocate(n), n}` now. Perhaps we should adopt this strategy for constant evaluation to avoid additional mechanism in the compiler.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll. Suggestion to fix this in Core instead.

Proposed resolution:

This wording is relative to N4910.

1. Modify 20.2.10.2 [allocator.members] as indicated:

   >     [[nodiscard]] constexpr allocation_result<T*> allocate_at_least(size_t n);

   > > -6- Mandates: `T` is not an incomplete type (6.9.1 [basic.types.general]).

   > > -7- Returns: `allocation_result<T*>{ptr, count}`, where `ptr` is a pointer to the initial element of an array of `count T` and `count ≥ n`.

   > > -8- Throws: `bad_array_new_length` if `numeric_limits<size_t>::max() / sizeof(T) < n`, or `bad_alloc` if the storage cannot be obtained.

   > > -9- Remarks: The storage for the array is obtained by calling `::operator new`, but it is unspecified when or how often this function is called. This function starts the lifetime of the array object, but not that of any of the array elements. This function returns `allocation_result<T*>{allocate(n), n}` within the evaluation of a core constant expression.

### 3685(i). In `lazy_split_view`, CTAD doesn't work when given an `input_range` input and a `tiny-range` pattern

Section: 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Konstantin Varlamov Opened: 2022-03-23 Last modified: 2022-05-17

Priority: 3

View other active issues in [range.lazy.split.view].

View all other issues in [range.lazy.split.view].

View all issues with New status.

Discussion:

In `lazy_split_view`, the deduction guide that accepts two arbitrary types wraps the arguments in `views::all_t` (25.7.16.2 [range.lazy.split.view]):

>     template<class R, class P>
>       lazy_split_view(R&&, P&&) -> lazy_split_view<views::all_t<R>, views::all_t<P>>;

When trying to use an `input_range` as the input, `lazy_split_view` requires the pattern type to satisfy the exposition-only concept `tiny-range`. Trying to use CTAD with an `input_range` and a `tiny-range` as arguments results in a compiler error, as demonstrated in the demo link:

>     // Assuming InputRange and TinyRange are valid types satisfying the
>     // corresponding concepts.
>     std::ranges::lazy_split_view view{InputRange(), TinyRange()}; // Compiler error

The underlying reason is that `tiny-range` requires the given type to contain a static member function `size()` that returns a number `<=1` (25.7.16.2 [range.lazy.split.view]):

>     template<class R>
>       concept tiny-range =                                          // exposition only
>         sized_range<R> &&
>         requires { typename require-constant<remove_reference_t<R>::size()>; } &&
>         (remove_reference_t<R>::size() <= 1);

However, when given a range, `views::all_t` wraps the type in a `ranges::owning_view`. `owning_view` doesn't satisfy `tiny-range` for any template parameter because it never contains the static `size()` function required by the concept.

A general resolution might be modifying `owning_view` so that it satisfies `tiny-range` when the given type is a `tiny-range` (that would require moving the `tiny-range` concept from 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers]). A more localized solution can be to change the deduction guide in `lazy_split_view` to avoid wrapping a type satisfying `tiny-range` in `views::all_t`.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll. One vote for NAD.

Proposed resolution:

This wording is relative to N4910.

1. Modify 25.5.2 [range.utility.helpers] as indicated:

   > [Drafting note: This change effectively just moves the definitions of `require-constant` and `tiny-range` from 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers].]

   >     […]
   >     template<class T, class U>
   >       concept different-from = // exposition only
   >         !same_as<remove_cvref_t<T>, remove_cvref_t<U>>;

   >     template<auto> struct require-constant; // exposition only

   >     template<class R>
   >     concept tiny-range = // exposition only
   >       sized_range<R> &&
   >       requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   >       (remove_reference_t<R>::size() <= 1);

2. Modify 25.7.6.3 [range.owning.view], class template `owning_view` synopsis, as indicated:

   >     […]
   >     constexpr static auto size() requires tiny-range<R>
   >     { return R::size(); }
   >     constexpr auto size() requires sized_range<R>
   >     { return ranges::size(r_); }
   >     constexpr auto size() const requires sized_range<const R>
   >     { return ranges::size(r_); }
   >     […]

3. Modify 25.7.16.2 [range.lazy.split.view], class template `lazy_split_view` synopsis, as indicated:

   > [Drafting note: This change effectively just moves the definitions of `require-constant` and `tiny-range` from 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers].]

   >     namespace std::ranges {
   >       template<auto> struct require-constant; // exposition only

   >       template<class R>
   >       concept tiny-range = // exposition only
   >         sized_range<R> &&
   >         requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   >         (remove_reference_t<R>::size() <= 1);

   >       template<input_range V, forward_range Pattern>
   >         requires view<V> && view<Pattern> &&
   >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
   >                  (forward_range<V> || tiny-range<Pattern>)
   >       class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> {
   >         […]
   >       };
   >       […]
   >     }

### 3688(i). Exception specifications of copy/move member functions of `std::bad_expected_access`

Section: 22.8.4 [expected.bad], 22.8.5 [expected.bad.void], 22.8.6.6 [expected.object.obs], 22.8.7.6 [expected.void.obs] Status: New Submitter: Jiang An Opened: 2022-03-24 Last modified: 2024-07-24

Priority: 2

View all issues with New status.

Discussion:

The move constructor and the move assignment operator of standard exception types are not covered by 17.9.3 [exception]/2, and thus it is currently effectively unspecified whether these move functions of `std::bad_expected_access<void>` are `noexcept`. Furthermore, `std::bad_expected_access<void>` has protected special member functions, which overrides (or conflicts with?) the general rule in 17.9.3 [exception]/2.

The primary template `std::bad_expected_access<E>` stores an `E` object, and copying the stored object may throw an exception. Is it intended that the copy functions of `std::bad_expected_access<E>` are `noexcept` while those of `E` are not? When the copy happens because a `std::bad_expected_access<E>` is caught by value, if the copy constructor of `E` throws, `std::terminate` is called no matter whether that of `std::bad_expected_access<E>` is `noexcept`. But `std::bad_expected_access<E>` may be copied/moved in other circumstances.

I think the move constructor and the move assignment operator of a standard exception type should be specified to be public and `noexcept` when they exist, although sometimes whether they exist may be unspecified. And the move functions should also propagate the result of `what()` when the source and target have the same dynamic type, except that they can leave the result of `what()` from the source valid but unspecified.

Related to this, `std::expected<T, E>::value` overloads are specified to throw `std::bad_expected_access<std::decay_t<E>>` when an `E` is contained. However, it seems that the copy constructor of `std::bad_expected_access` is implicitly deleted if `E` is move-only, so the throw-expression is ill-formed.

Is it intended that `std::expected<T, E>::value` is ill-formed in such cases?

[2022-05-17; Reflector poll]

Set priority to 2 after reflector poll.

[2023-05-25; Jonathan comments]

The last part was clarified by LWG 3843(i), confirming that `value()` is ill-formed for move-only `E`.

[2024-07-24; Jonathan comments]

LWG 4031(i) made the move (and copy) operations of `bad_expected_access<void>` non-throwing.

Proposed resolution:

### 3689(i). `num_get` overflow determination unclear and incorrect

Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: New Submitter: Hubert Tong Opened: 2022-03-28 Last modified: 2022-05-17

Priority: 3

View other active issues in [facet.num.get.virtuals].

View all other issues in [facet.num.get.virtuals].

View all issues with New status.

Discussion:

28.3.4.3.2.3 [facet.num.get.virtuals] stage 3 specifies that "by the rules of" various `strto*` functions, sequences of chars are converted to a numeric value (producing the "converted value" that is referred to).

It then goes on to specify various error cases when the "field represents a value" that is outside the range of representable values. Clearly, the value that would be returned from the appropriate `strto*` function is not outside the range of representable values for `long long`, `unsigned long long`, `float`, `double`, and `long double`; therefore (unless if we expect no range-related errors for those types), the field "represents a value" other than the "converted value".

Issue 1: It is too subtle to have two distinct values without calling more attention to them by giving them names aside from the prose descriptions.

If the field "represents" a value other than the value that would be returned from the appropriate `strto*` function, then what value does the field "represent"? Note that, strictly speaking, it is the process that results in the converted value that the wording says is obtained "by the rules of" the `strto*` functions, which is not the same thing as saying that the value represented is interpreted "by the rules of" the `strto*` functions.

If the field "represents" the mathematical value, then for unsigned integer types, all negative values cannot be represented. This does not match existing practice.

If negative integer values are interpreted using the rules of the `strto*` functions by obtaining the magnitude and then having it "negated (in the return type)", then the return type of `strtoull` is `unsigned long long`, meaning (where `unsigned long long` is 64-bit) that "`-18446744073709551615`" is `1` (even when converting to unsigned integer types of less width). That does not match existing practice. It is also worth noting that negating in the return type does not work well if the magnitude is not representable as a positive value in the return type (e.g., for signed integer types and their most negative representable values).

Issue 2: The effect of the minus sign with respect to unsigned integer types can reasonably be interpreted in ways that do not match existing practice (and are presumably unintended). The interpretation that works better for `unsigned long long` does not work as well for `signed long long`.

If the field does not "represent" the mathematical value, then for floating-point types, it is unclear whether the minus sign takes effect before or after any possible rounding. For literals, the minus sign takes effect after rounding.

Issue 3: The effect of the minus sign with respect to floating-point types is unclear.

Lastly, for floating-point types with signed infinities, there are no finite values outside the range of representable values; therefore, conversions of all finite values to such types are specified to "succeed". That does not match existing implementation practice.

Issue 4: The conditions for identifying range-related errors for conversions to floating-point types do not match the conditions that constitute overflow for floating-point types. There is implementation divergence: libc++ appears to check for floating-point overflow; libstdc++ appears to check for infinities.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3690(i). `std::make_from_tuple` etc. should find all tuple-like `std::get` overloads

Section: 16.4.2.2 [contents], 22.4.6 [tuple.apply] Status: New Submitter: Jiang An Opened: 2022-04-06 Last modified: 2022-05-17

Priority: 3

View all other issues in [contents].

View all issues with New status.

Discussion:

Currently it is not clear in 16.4.2.2 [contents]/3 whether all possible overloads in the standard library are considered to be found "in the context of `D`". As a result, it seems underspecified whether a certain `std::get` overload is found by `std::tuple_cat`, `std::make_from_tuple`, `std::apply`, or exposition-only concept `pair-like` or `has-tuple-element`.

There is implementation divergence: MSVC STL's `std::make_from_tuple` accepts `std::ranges::subrange`, but libstdc++'s doesn't, which is originally discussed in GCC bug #102301.

IMO `std::get` overloads need some special rules: when referred by tuple-like facilities, overloads for `std::variant` should be excluded (or at least leave whether it's found unspecified), and all other overloads should be found; and the opposite rule should be used when referred in 22.6 [variant].

[2022-04-25; Jiang An comments and provides wording]

Currently this program is accepted when using MSVC STL and libstdc++, although the acception seems unintended and problematic.

>     #include <variant>
>     #include <span>
>     #include <ranges>

>     struct Foo : std::variant<int, long> {};

>     template<>
>     struct std::tuple_element<0, Foo> { using type = int; };

>     template<>
>     struct std::tuple_element<1, Foo> { using type = long; };

>     template<>
>     struct std::tuple_size<Foo> : std::integral_constant<std::size_t, 2> {};

>     constexpr auto bad_keys = std::span<Foo>{} | std::views::values;

>     int main() {} // COMPILE-ONLY

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4910.

1. Modify 16.4.2.2 [contents] as indicated:

   > […]

   > -3- Whenever an unqualified name other than `swap` is used in the specification of a declaration `D` in Clause 17 [support] through Clause 32 [thread] or Annex D [depr], its meaning is established as-if by performing unqualified name lookup (6.5.3 [basic.lookup.unqual]) in the context of `D`.

   > [Note 1: Argument-dependent lookup is not performed. — end note]

   > Similarly, the meaning of a qualified-id is established as-if by performing qualified name lookup (6.5.5 [basic.lookup.qual]) in the context of `D`.

   > [Example 1: The reference to `is_array_v` in the specification of `std::to_array` (23.3.3.6 [array.creation]) refers to `::std::is_array_v`. — end example]

   > [Note 2: Operators in expressions (12.2.2.3 [over.match.oper]) are not so constrained; see 16.4.6.4 [global.functions]. — end note] The meaning of the unqualified name `swap` is established in an overload resolution context for swappable values (16.4.4.3 [swappable.requirements]).

   > Certain entities in the standard library are specified to select tuple-like `get` function templates. An implementation shall behave as if every tuple-like `get` function template is found in the definition of such an entity. Furthermore, an implementation shall ensure that no `get` function template that is not tuple-like is found in the definition of such an entity.

2. Add to the end of 22.3.4 [pair.astuple], 22.4.8 [tuple.elem], 23.3.3.7 [array.tuple], and 25.5.4.3 [range.subrange.access] as indicated:

   > The `get` function templates specified in this section are tuple-like (16.4.2.2 [contents]).

3. Modify 22.4.6 [tuple.apply] as indicated:

   >     template<class F, class Tuple>
   >       constexpr decltype(auto) apply(F&& f, Tuple&& t);

   > > -1- Effects: Given the exposition-only function:

   > > >     namespace std {
   > > >       template<class F, class Tuple, size_t... I>
   > > >       constexpr decltype(auto) apply-impl(F&& f, Tuple&& t, index_sequence<I...>) { // exposition only
   > > >         return INVOKE(std::forward<F>(f), get<I>(std::forward<Tuple>(t))...); // see 22.10.4 [func.require]
   > > >       }
   > > >     }

   > > Equivalent to:

   > > >     return apply-impl(std::forward<F>(f), std::forward<Tuple>(t),
   > > >                       make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});

   > > -?- Remarks: `apply-impl` selects tuple-like `get` function templates.

   >     template<class T, class Tuple>
   >       constexpr T make_from_tuple(Tuple&& t);

   > > -2- Mandates: If `tuple_size_v<remove_reference_t<Tuple>>` is `1`, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false`.

   > > -3- Effects: Given the exposition-only function:

   > > >     namespace std {
   > > >       template<class T, class Tuple, size_t... I>
   > > >         requires is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...>
   > > >       constexpr T make-from-tuple-impl(Tuple&& t, index_sequence<I...>) { // exposition only
   > > >         return T(get<I>(std::forward<Tuple>(t))...);
   > > >       }
   > > >     }

   > > Equivalent to:

   > > >     return make-from-tuple-impl<T>(
   > > >               std::forward<Tuple>(t),
   > > >               make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});

   > > […]

   > > -?- Remarks: `make-from-tuple-impl` selects tuple-like `get` function templates.

4. Add at the end of 25.5.4.1 [range.subrange.general] (after the synopsis) as indicated:

   > [Drafting note: Although IIUC `pair-like` is not needed to handle `array` and `subrange`.]

   > […]

   > -?- Remarks: `pair-like` selects tuple-like `get` function templates.

5. Add after the synopsis of 25.7.23.2 [range.elements.view] as indicated:

   > […]

   > -?- Remarks: `has-tuple-element` selects tuple-like `get` function templates.

   > […]

### 3691(i). Replacement of keys in associative containers

Section: 23.2.7.1 [associative.reqmts.general], 23.2.8.1 [unord.req.general] Status: New Submitter: Jens Maurer Opened: 2022-04-19 Last modified: 2022-05-17

Priority: 3

View other active issues in [associative.reqmts.general].

View all other issues in [associative.reqmts.general].

View all issues with New status.

Discussion:

Keys for elements of associative containers are presented as `const` subobjects, preventing their modification by user code according to 9.2.9.2 [dcl.type.cv] p4.

However, that does not prevent those keys to be transparently replaced, for example via

>     std::map<int, int> map;
>     map.emplace(1, 2);
>     using KT = std::map<int, int>::key_type;
>     auto it = map.begin();
>     it->first.~KT();
>     new (const_cast<int*>(&it->first)) KT(3);

This, of course, breaks the ordering of the keys, and should be undefined behavior.

Related issue: CWG 2514.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll. One vote for NAD.

Proposed resolution:

This wording is relative to N4910.

1. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   > -5- For `set` and `multiset` the value type is the same as the key type. For `map` and `multimap` it is equal to `pair<const Key, T>`. Ending the lifetime of the key subobject of a container element by means other than invoking a member function of the container results in undefined behavior.

2. Modify 23.2.8.1 [unord.req.general] as indicated:

   > -7- For `unordered_set` and `unordered_multiset` the value type is the same as the key type. For `unordered_map` and `unordered_multimap` it is `pair<const Key, T>`. Ending the lifetime of the key subobject of a container element by means other than invoking a member function of the container results in undefined behavior.

### 3693(i). §[c.math] Can any of `float/double/long double` overloads be fused into template overloads?

Section: 29.7 [c.math] Status: New Submitter: Jiang An Opened: 2022-04-28 Last modified: 2022-05-17

Priority: 2

View all other issues in [c.math].

View all issues with New status.

Discussion:

IIUC LWG 3234(i) will be resolved by the recently approved paper P1467R9. While considering adding the newly required overloads of math special functions to MSVC STL, I found that it may be more convenient to implement the whole overload set as a single function template.

However, it's unclear for me whether the every "overload for each cv-unqualified floating-point type", or every currently separately shown overloads in the synopsis of `<cmath>`, is required to be a separated function. As discussed in microsoft/STL#1335, if there were only a separated `double` overload (usually comes from the C standard library) and a fused template overload, calling the overload set with `{}` would be accepted, which is definitely ambiguous when there are separated `float/double/long double` overloads.

I think it may be better to allow implementations to arbitrarily fuse the required overloads.

[2022-05-17; Reflector poll]

Set priority to 2 after reflector poll. One vote for NAD.

Proposed resolution:

### 3694(i). Should `traits_type::length` be customizable?

Section: 27.2.2 [char.traits.require] Status: New Submitter: Jiang An Opened: 2022-05-04 Last modified: 2022-05-17

Priority: 4

View other active issues in [char.traits.require].

View all other issues in [char.traits.require].

View all issues with New status.

Discussion:

MSVC STL's implementation of the `std::quoted` overload for `const charT*` calculates the length of the NTCTS and stores the result within the return value. Because the returned value may be output by `std::basic_ostream` specializations with different `traits_type`s, this strategy can be conforming only if all possible `traits_type::length` functions for the same `char_type` have equivalent return values.

It seems not clear whether `traits_type::length` should be customizable. In a related PR, Stephan T. Lavavej said :

> I argue that you've found a defect in the `char_traits` specification — it should say that whatever `eq()` does, it should consider `charT()` to be distinct from all other values, which aligns with the common understanding of how null-terminated strings behave.

> The original implementation and the `char_traits::length` change both handle arbitrary character types — the only difference would be for custom `char_traits` that consider null terminators to be equal to other values, which I have never seen used in practice (e.g. case-insensitive traits don't do this).

If it is decided that `traits_type::length` is customizable, then the implementation in MSVC STL should be fixed. Otherwise, we should explicitly require in 27.2.2 [char.traits.require] that whenever `c` is not "equal" to `char_type()`, `traits_type::eq(c, char_type())` is `false`.

However, equivalence of two `charT` values seems not easy to specify, as there may be no `operator==` for `charT`, or the `operator==` behaves pathologically. IMO possible way may be

- using the equivalence relation of value representations, or

- using `==` and only imposing the requirement when `char_type` is an encoded character type (or more generally, a scalar type, while treating an enum type as its underlying type).

[2022-05-17; Reflector poll]

Set priority to 4 after reflector poll. One vote for NAD.

Proposed resolution:

### 3696(i). "Basic integral types" should not be used

Section: 31.2.2 [stream.types] Status: Ready Submitter: Jiang An Opened: 2022-05-07 Last modified: 2026-06-15

Priority: 3

View all issues with Ready status.

Discussion:

Raised from editorial issue #5240.

The phrase "signed basic integral types" in 31.2.2 [stream.types] has been present since C++98 but never defined. It is unclear whether "basic integral types" are "standard integer types" or "integer types" (including extended integer types).

As `std::streamoff` should be wide enough to represent the largest possible file size, and `std::uintmax_t` is used as the return type of `std::filesystem::file_size`, we should not disallow `std::streamoff` to be an extended integer type which may be wider than `long long`. On the other hand, as `std::size_t` and `std::ptrdiff_t` have already been allowed to be extended integer types, `std::streamsize` should also be allowed to be an extended integer type for consistency.

So I think we should just use "signed integer types" instead of "signed basic integral types" in 31.2.2 [stream.types].

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

[2025-06-16; Jonathan adds wording]

Previous resolution [SUPERSEDED]

This wording is relative to N5008.

1. Modify 31.2.2 [stream.types] as indicated:

   >     using streamoff = implementation-defined;

   > -1- The type `streamoff` is a synonym for one of the signed basic integral types of sufficient size to represent the maximum possible file size for the operating system.256

   >     using streamsize = implementation-defined;

   > -2- The type `streamsize` is a synonym for one of the signed basic integral types. It is used to represent the number of characters transferred in an I/O operation, or the size of I/O buffers.257

   > 256) Typically `long long`.

   > 257) Most places where `streamsize` is used would use `size_t` in C, or `ssize_t` in POSIX.

[2025-10-21; Jonathan provides improved wording]

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5008.

1. Modify 31.2.2 [stream.types] as indicated:

   >     using streamoff = implementation-defined;

   > -1- The type `streamoff` is a synonym for one of the signed integer basic integral types of sufficient size to represent the maximum possible file size for the operating system.256

   >     using streamsize = implementation-defined;

   > -2- The type `streamsize` is a synonym for one of the signed integer basic integral types. It is used to represent the number of characters transferred in an I/O operation, or the size of I/O buffers.257

   > 256) Typically `long long`.

   > 257) Most places where `streamsize` is used would use `size_t` in C, or `ssize_t` in POSIX.

### 3697(i). Preconditions of `reference_constructs_from_temporary/reference_converts_from_temporary` seem wrong

Section: 21.3.5.4 [meta.unary.prop] Status: New Submitter: Jiang An Opened: 2022-05-10 Last modified: 2022-05-17

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

`std::reference_constructs_from_temporary` and `std::reference_converts_from_temporary` are only useful when `T` is a reference type, and a reference type is always complete. Whenever `T` is an incomplete object type, it's clear that these traits inherit from `std::false_type`, without any further detection which may result in UB. However, when `T` is `X&` or `X&&` where `X` is an incomplete object type, UB may be needed because of the potentially problematic detection in `std::is_constructible` or `std::is_convertible`.

I'm not sure whether the general rule in 21.3.2 [meta.rqmts]/5 affects these cases.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4910.

1. Modify 21.3.3 [meta.type.synop], Table 46 ([tab:meta.unary.prop]) — "Type property predicates" — as indicated:

   > Table 46: Type property predicates [tab:meta.unary.prop]
   > Template | Condition | Preconditions
   > --- | --- | ---
   > `…`
   > `template<class T, class U> struct reference_constructs_from_temporary;` | `conjunction_v<is_reference<T>, is_constructible<T, U>>` is `true`, and the initialization `T t(VAL<U>);` binds `t` to a temporary object whose lifetime is extended (6.8.7 [class.temporary]). | If `T` is a reference type, `remove_reference_t<T>` and `U` `T` shall be a complete types, cv `void`, or an arrays of unknown bound.
   > `template<class T, class U> struct reference_converts_from_temporary;` | `conjunction_v<is_reference<T>, is_convertible<U, T>>` is `true`, and the initialization `T t = VAL<U>;` binds `t` to a temporary object whose lifetime is extended (6.8.7 [class.temporary]). | If `T` is a reference type, `remove_reference_t<T>` and `U` shall be complete types, cv `void`, or arrays of unknown bound.

### 3699(i). `lexically_relative` on UNC drive paths (`\\?\C:\...`) results in a default-constructed value

Section: 31.12.6.5.11 [fs.path.gen] Status: New Submitter: Nicole Mazzuca Opened: 2022-05-12 Last modified: 2022-05-17

Priority: 3

View all other issues in [fs.path.gen].

View all issues with New status.

Discussion:

As a resolution to LWG 3070(i), in `path lexically_relative(const path& base) const`, bullet 3.4 was added:

> If: […] any filename in `relative_path()` or `base.relative_path()` can be interpreted as a root-name, […] returns `path()`.

This resolution was correct when we have really weird paths like `abc\X:\c`, but the MSVC standard library implementation treats UNC drive-relative paths as:

>     \\?\C:\foo\bar = { root-name = \\?, root-directory = \, relative-path = C:\foo\bar }

If we were able to go back in time, we might have `root-name = \\?\C:`, but we can't make that change at that point without silently breaking users; therefore, we believe it would be best to instead change `lexically_relative()` to work around this issue.

There exists a related github issue.

I don't yet have standard wording, but I think it would be reasonable to do something like:

> If `relative_path().has_root_path() && base.relative_path().has_root_path()`, and `relative_path().root_path() == base.relative_path().root_path()`, then `return relative_path().lexically_relative(base.relative_path())`.

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3706(i). How does `std::format` work with character arrays of unknown bound?

Section: 28.5.6.4 [format.formatter.spec] Status: New Submitter: S. B. Tam Opened: 2022-05-31 Last modified: 2022-06-21

Priority: 3

View other active issues in [format.formatter.spec].

View all other issues in [format.formatter.spec].

View all issues with New status.

Discussion:

Consider

>     #include <format>
>     #include <iostream>

>     extern char str[];
>     auto result = std::format("{}", str);
>     char str[] = "hello";

>     int main()
>     {
>       std::cout << result << std::endl;
>     }

Currently MSVC STL (as well as fmtlib when `fmt::format` is used instead of `std::format`) accepts the initializer of `result`, while libc++ produces an error (apparently because there's no `formatter` for `char[]`).

Should this be valid?

Daniel:

This issue is similar to LWG 3701(i), but not the same, because the latter wants `template<size_t N> struct formatter<charT[N], charT>`, while this one needs `template<>struct formatter<charT[], charT>` (that is, without the array bound).

[2022-06-21; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3716(i). §[iterator.concept.forward][forward.iterators] Two different definitions of multi-pass guarantee

Section: 24.3.4.11 [iterator.concept.forward], 24.3.5.5 [forward.iterators] Status: New Submitter: Jiang An Opened: 2022-06-15 Last modified: 2022-07-06

Priority: 3

View all issues with New status.

Discussion:

There are two different definitions of multi-pass guarantee since P0896R4. The old one (perhaps introduced by N3066) seems less reasonable because it requires increment on rvalue iterators of class types, and the semantics of such increment is largely unspecified.

Perhaps only the new definition should be used.

[2022-07-06; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3725(i). `reverse_iterator::operator->` should not use `prev` for non-pointer iterators

Section: 24.5.1.6 [reverse.iter.elem] Status: New Submitter: Hewill Kang Opened: 2022-06-26 Last modified: 2024-06-18

Priority: 3

View all other issues in [reverse.iter.elem].

View all issues with New status.

Discussion:

When the underlying iterator is not a pointer type, `reverse_iterator::operator->` returns `prev(current).operator->()`. However, `prev` only works with Cpp17BidirectionalIterator, given that C++20 `bidirectional_iterator` may just be Cpp17InputIterator, we shouldn't use `prev` here.

[2022-07-08; Reflector poll]

Set priority to 3 after reflector poll. Suggested to use `ranges::prev` instead.

[2024-06-18; Jonathan adds a comment]

It's not clear that `std::prev` requires Cpp17InputIterator, that's the subject of LWG 3197(i).

Proposed resolution:

This wording is relative to N4910.

1. Modify 24.5.1.6 [reverse.iter.elem] as indicated:

   >     constexpr pointer operator->() const
   >       requires (is_pointer_v<Iterator> ||
   >                 requires(const Iterator i) { i.operator->(); });

   > > -2- Effects:

   > > 1. (2.1) — If `Iterator` is a pointer type, equivalent to: `return prev(current);`

   > > 2. (2.2) — Otherwise, equivalent to: `return prev(current).operator->();`

   > >    >     Iterator tmp = current;
   > >    >     --tmp;
   > >    >     return tmp.operator->();

### 3728(i). Can't make neither head nor tail of the description of `operator<=>(tuple, tuple)`

Section: 22.4.9 [tuple.rel] Status: New Submitter: Corentin Jabot Opened: 2022-06-28 Last modified: 2022-07-08

Priority: 4

View other active issues in [tuple.rel].

View all other issues in [tuple.rel].

View all issues with New status.

Discussion:

The specification of `operator<=>(tuple, tuple)` (22.4.9 [tuple.rel]) is described in terms of imaginary tuples (`ttail`, `utail`, `rtail`) which is a bit confusing. Indeed, It is not clear that these imaginary tuples need to respect the order of elements of `u` and `t`, nor whether the value category of the elements in these imaginary tuples can or should be conserved. It is possible to reformulate and simplify that description so that no imaginary tuple is involved.

The remark is copied from the similar wording of `operator==`

[2022-07-08; Reflector poll]

Set priority to 4 after reflector poll. Some votes for NAD and preference for the current wording, adding "in order" to clarify the order of elements in `rtail`.

Proposed resolution:

This wording is relative to N4910.

1. Modify 22.4.9 [tuple.rel] as indicated:

   >     template<class... TTypes, class... UTypes>
   >       constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
   >         operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

   > > -4- EffectsReturns: `synth-three-way(get<i>(t), get<i>(u))` for the first `i` for which the result of that expression does not compare equal to `0`. If no such `i` exists, `strong_ordering::equal`.Performs a lexicographical comparison between `t` and `u`. For any two zero-length tuples `t` and `u`, `t <=> u` returns `strong_ordering::equal`. Otherwise, equivalent to:

   > > >     if (auto c = synth-three-way(get<0>(t), get<0>(u)); c != 0) return c;
   > > >     return ttail <=> utail;

   > > where `rtail` for some tuple `r` is a tuple containing all but the first element of `r`.

   > > -?- Remarks: The elementary `synth-three-way(get<i>(t), get<i>(u))` expressions are evaluated in order from the zeroth index upwards. No element accesses are performed after the first invocation that results in a value that does not compare equal to `0`.

   > > -5- [Note 1: The above definition does not require `ttail` (or `utail`) to be constructed. It might not even be possible, as `t` and `u` are not required to be copy constructible. Also, all comparison operator functions are short circuited; they do not perform element accesses beyond what is required to determine the result of the comparison. — end note]

### 3729(i). `std::tuple_element_t<std::ranges::subrange<I, S, K>>` should remove top-level cv-qualifiers

Section: 25.2 [ranges.syn] Status: New Submitter: Jiang An Opened: 2022-06-30 Last modified: 2022-07-08

Priority: 4

View other active issues in [ranges.syn].

View all other issues in [ranges.syn].

View all issues with New status.

Discussion:

`std::ranges::subrange<int * volatile>>` is weird but valid. The return type (deduced type for `auto`) of `std::ranges::get` for this type is `int*`, because the replacing of that (cv-unqualified) `auto` drops top-level cv-qualifiers. I think `get` is doing the right thing, and `std::tuple_element_t` should be consistent with the return types.

[2022-07-08; Reflector poll]

Set priority to 4 after reflector poll. "This is just contrived, especially since lots of the iterator requirements on such a type involve deprecated operations."

Proposed resolution:

This wording is relative to N4910.

1. Modify 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     […]
   >     namespace std {
   >       […]

   >       template<class I, class S, ranges::subrange_kind K>
   >       struct tuple_element<0, ranges::subrange<I, S, K>> {
   >         using type = remove_cv_t<I>;
   >       };
   >       template<class I, class S, ranges::subrange_kind K>
   >       struct tuple_element<1, ranges::subrange<I, S, K>> {
   >         using type = remove_cv_t<S>;
   >       };
   >       template<class I, class S, ranges::subrange_kind K>
   >       struct tuple_element<0, const ranges::subrange<I, S, K>> {
   >         using type = remove_cv_t<I>;
   >       };
   >       template<class I, class S, ranges::subrange_kind K>
   >       struct tuple_element<1, const ranges::subrange<I, S, K>> {
   >         using type = remove_cv_t<S>;
   >       };
   >     }
   >     […]

### 3730(i). `std::ranges::drop_view` may have different size type from its underlying view

Section: 25.7.12.2 [range.drop.view] Status: New Submitter: Jiang An Opened: 2022-07-03 Last modified: 2022-07-17

Priority: 3

View other active issues in [range.drop.view].

View all other issues in [range.drop.view].

View all issues with New status.

Discussion:

The bodies of both overloads of `drop_view<V>::size` are specified as:

>     const auto s = ranges::size(base_);
>     const auto c = static_cast<decltype(s)>(count_);
>     return s < c ? 0 : s - c;

Given the return type is specified with `auto`, the actual return type is the promoted type of the size type of the underlying view, which may be different from the underlying size type (e.g. if the underlying size is `unsigned short`).

Note that `take_view` always has the same size type as its underlying view. So I think the difference on the size types is an oversight. On the other hand, the `const` used here seems redundant and inconsistent with other parts of the standard wording, although implementations may tend to use it.

[2022-07-08; Reflector poll]

Set priority to 3 after reflector poll.

"The PR is incorrect - integer-class types do not support mixed-signedess operations, so you have to cast one of the two first."

[2022-07-17; Daniel comments]

This issue should be resolved by keeping LWG 3739(i) and 3740(i) in mind.

Proposed resolution:

This wording is relative to N4910.

1. Modify 25.7.12.2 [range.drop.view], class template `drop_view` synopsis, as indicated:

   > [Drafting note: `s` and `count_` usually have different types, but I think it's safe to perform comparison and subtraction, as `count_` is non-negative as long as the behavior is well-defined.]

   >     […]
   >     constexpr auto size() requires sized_range<V> {
   >       const auto s = ranges::size(base_);
   >       const auto c = static_cast<decltype(s)>(count_);
   >       return static_cast<decltype(s)>(s < ccount_ ? 0 : s - ccount_);
   >     }

   >     constexpr auto size() const requires sized_range<const V> {
   >       const auto s = ranges::size(base_);
   >       const auto c = static_cast<decltype(s)>(count_);
   >       return static_cast<decltype(s)>(s < ccount_ ? 0 : s - ccount_);
   >     }
   >     […]

### 3731(i). `zip_view` and `adjacent_view` are underconstrained

Section: 25.7.25.2 [range.zip.view], 25.7.27.2 [range.adjacent.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2022-07-04 Last modified: 2023-08-12

Priority: 3

View other active issues in [range.zip.view].

View all other issues in [range.zip.view].

View all issues with New status.

Discussion:

Both `zip_view::iterator`'s (25.7.25.3 [range.zip.iterator]) and `adjacent_view::iterator`'s (25.7.27.3 [range.adjacent.iterator]) `operator*` have similar Effects: elements:

>     return tuple-transform([](auto& i) -> decltype(auto) { return *i; }, current_);

where `tuple-transform` is defined as:

>     template<class F, class Tuple>
>     constexpr auto tuple-transform(F&& f, Tuple&& tuple) { // exposition only
>       return apply([&]<class... Ts>(Ts&&... elements) {
>         return tuple-or-pair<invoke_result_t<F&, Ts>...>(
>           invoke(f, std::forward<Ts>(elements))...
>         );
>       }, std::forward<Tuple>(tuple));
>     }

That is, `zip_view::iterator` will invoke the `operator*` of each iterator of `Views` and return a `tuple` containing its `reference`.

This is not a problem when the `reference` of iterators is actually the reference type. However, when the `operator*` returns a prvalue of non-movable type, `tuple-transform` will be ill-formed since there are no suitable constructors for `tuple`:

>     #include <ranges>

>     struct NonMovable {
>       NonMovable() = default;
>       NonMovable(NonMovable&&) = delete;
>     };

>     auto r = std::views::iota(0, 5)
>            | std::views::transform([](int) { return NonMovable{}; });
>     auto z = std::views::zip(r);
>     auto f = *z.begin(); // hard error

We should constrain the `range_reference_t` of the underlying range to be `move_constructible` when it is not a reference type, which also solves similar issues in `zip_view::iterator` and `adjacent_view::iterator`'s `operator[]` and `iter_move`.

[2022-08-23; Reflector poll]

Set priority to 3 after reflector poll. "The constraint should just be `move_constructible`."

Previous resolution [SUPERSEDED]

This wording is relative to N4910.

1. Modify 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     namespace std::ranges {
   >       […]
   >       // 25.7.25 [range.zip], zip view
   >       template<class Ref>
   >         concept tuple-constructible-reference = see below; // exposition only

   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (tuple-constructible-reference<range_reference_t<Views>> && ...)
   >       class zip_view;

   >       […]

   >       // 25.7.27 [range.adjacent], adjacent view
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) &&
   >                  tuple-constructible-reference<range_reference_t<V>>
   >       class adjacent_view;
   >     }
   >     […]

2. Modify 25.7.25.2 [range.zip.view] as indicated:

   >     namespace std::ranges {
   >       template<class Ref>
   >         concept tuple-constructible-reference =            // exposition only
   >           is_reference_v<Ref> || move_constructible<Ref>;
   >       […]
   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (tuple-constructible-reference<range_reference_t<Views>> && ...)
   >       class zip_view : public view_interface<zip_view<Views...>> {
   >         […]
   >       };
   >       […]
   >     }

3. Modify 25.7.27.2 [range.adjacent.view] as indicated:

   >     namespace std::ranges {
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) &&
   >                  tuple-constructible-reference<range_reference_t<V>>
   >       class adjacent_view : public view_interface<adjacent_view<V, N>> {
   >         […]
   >       };
   >       […]
   >     }

[2022-09-25; Hewill provides improved wording]

Previous resolution [SUPERSEDED]

This wording is relative to N4917.

1. Modify 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     namespace std::ranges {
   >       […]

   >       template<class R>
   >         concept has-tuplable-ref =            // exposition only
   >           move_constructible<range_reference_t<R>>;

   >       // 25.7.25 [range.zip], zip view
   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (has-tuplable-ref<Views> && ...)
   >       class zip_view;

   >       […]

   >       // 25.7.27 [range.adjacent], adjacent view
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) && has-tuplable-ref<V>
   >       class adjacent_view;

   >       […]

   >       // 25.7.33 [range.cartesian], cartesian product view
   >       template<input_range First, forward_range... Vs>
   >         requires (view<First> && ... && view<Vs>) &&
   >                  (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>)
   >       class cartesian_product_view;

   >       […]
   >     }

2. Modify 25.7.25.2 [range.zip.view] as indicated:

   >     namespace std::ranges {
   >       […]

   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (has-tuplable-ref<Views> && ...)
   >       class zip_view : public view_interface<zip_view<Views...>> {
   >         […]
   >       };
   >     }

3. Modify 25.7.25.3 [range.zip.iterator] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (has-tuplable-ref<Views> && ...)
   >       template<bool Const>
   >       class zip_view<Views...>::iterator {
   >         […]
   >       };
   >     }

4. Modify 25.7.25.4 [range.zip.sentinel] as indicated:

   >     namespace std::ranges {
   >       template<input_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   >                  (has-tuplable-ref<Views> && ...)
   >       template<bool Const>
   >       class zip_view<Views...>::sentinel {
   >         […]
   >       };
   >     }

5. Modify 25.7.27.2 [range.adjacent.view] as indicated:

   >     namespace std::ranges {
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) && has-tuplable-ref<V>
   >       class adjacent_view : public view_interface<adjacent_view<V, N>> {
   >         […]
   >       };
   >     }

6. Modify 25.7.27.3 [range.adjacent.iterator] as indicated:

   >     namespace std::ranges {
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) && has-tuplable-ref<V>
   >       template<bool Const>
   >       class adjacent_view<V, N>::iterator {
   >         […]
   >       };
   >     }

7. Modify 25.7.27.4 [range.adjacent.sentinel] as indicated:

   >     namespace std::ranges {
   >       template<forward_range V, size_t N>
   >         requires view<V> && (N > 0) && has-tuplable-ref<V>
   >       template<bool Const>
   >       class adjacent_view<V, N>::sentinel {
   >         […]
   >       };
   >     }

8. Modify 25.7.33.2 [range.cartesian.view] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<input_range First, forward_range... Vs>
   >         requires (view<First> && ... && view<Vs>) &&
   >                  (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>)
   >       class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> {
   >         […]
   >       };
   >     }

9. Modify 25.7.33.3 [range.cartesian.iterator] as indicated:

   >     namespace std::ranges {
   >       template<input_range First, forward_range... Vs>
   >         requires (view<First> && ... && view<Vs>) &&
   >                  (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>)
   >       template<bool Const>
   >       class cartesian_product_view<First, Vs...>::iterator {
   >         […]
   >       };
   >     }

[2023-08-08; Hewill provides improved wording]

Proposed resolution:

This wording is relative to N4950.

1. Modify 26.2 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     namespace std::ranges {
   >       […]

   >       // 25.5.2 [range.utility.helpers], helper concepts
   >       template<class R>
   >         concept range-with-movable-references = see below; // exposition only

   >       // 25.5.3 [view.interface], class template view_interface
   >       template<class D>
   >         requires is_class_v<D> && same_as<D, remove_cv_t<D>>
   >       class view_interface;                                                             // freestanding

   >       […]

   >       // 25.7.24 [range.enumerate], enumerate view
   >       template<input_rangeview V>
   >         requires range-with-movable-references<V>view<View>
   >       class enumerate_view;                                                             // freestanding

   >       […]

   >       // 25.7.25 [range.zip], zip view
   >       template<range-with-movable-referencesinput_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0)
   >       class zip_view;                                                                   // freestanding

   >       […]

   >       // 25.7.27 [range.adjacent], adjacent view
   >       template<range-with-movable-referencesforward_range V, size_t N>
   >         requires forward_range<V> && view<V> && (N > 0)
   >       class adjacent_view;                                                              // freestanding

   >       […]

   >       // 25.7.33 [range.cartesian], cartesian product view
   >       template<range-with-movable-referencesinput_range First,
   >                range-with-movable-referencesforward_range... Vs>
   >         requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
   >       class cartesian_product_view;                                                     // freestanding

   >       […]
   >     }

2. Modify 25.7.25.2 [range.zip.view] as indicated:

   >     namespace std::ranges {
   >       […]

   >       template<range-with-movable-referencesinput_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0)
   >       class zip_view : public view_interface<zip_view<Views...>> {
   >         […]
   >       public:
   >         […]
   >         constexpr auto begin() const requires (range-with-movable-referencesrange<const Views> && ...) {
   >           return iterator<true>(tuple-transform(ranges::begin, views_));
   >         }
   >         […]
   >         constexpr auto end() const requires (range-with-movable-referencesrange<const Views> && ...) {
   >           if constexpr (!zip-is-common<const Views...>) {
   >             return sentinel<true>(tuple-transform(ranges::end, views_));
   >           } else if constexpr ((random_access_range<const Views> && ...)) {
   >             return begin() + iter_difference_t<iterator<true>>(size());
   >           } else {
   >             return iterator<true>(tuple-transform(ranges::end, views_));
   >           }
   >         }
   >         […]
   >       };
   >     }

3. Modify 25.7.25.3 [range.zip.iterator] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<range-with-movable-referencesinput_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0)
   >       template<bool Const>
   >       class zip_view<Views...>::iterator {
   >         […]
   >       };
   >     }

4. Modify 25.7.25.4 [range.zip.sentinel] as indicated:

   >     namespace std::ranges {
   >       template<range-with-movable-referencesinput_range... Views>
   >         requires (view<Views> && ...) && (sizeof...(Views) > 0)
   >       template<bool Const>
   >       class zip_view<Views...>::sentinel {
   >         […]
   >       };
   >     }

5. Modify 25.7.27.2 [range.adjacent.view] as indicated:

   >     namespace std::ranges {
   >       template<range-with-movable-referencesforward_range V, size_t N>
   >         requires forward_range<V> && view<V> && (N > 0)
   >       class adjacent_view : public view_interface<adjacent_view<V, N>> {
   >         […]
   >       public:
   >         […]
   >         constexpr auto begin() const requires range-with-movable-referencesrange<const V> {
   >           return iterator<true>(ranges::begin(base_), ranges::end(base_));
   >         }
   >         […]
   >         constexpr auto end() const requires range-with-movable-referencesrange<const V> {
   >           if constexpr (common_range<const V>) {
   >             return iterator<true>(as-sentinel{}, ranges::begin(base_), ranges::end(base_));
   >           } else {
   >             return sentinel<true>(ranges::end(base_));
   >           }
   >         }
   >         […]
   >       };
   >     }

6. Modify 25.7.27.3 [range.adjacent.iterator] as indicated:

   >     namespace std::ranges {
   >       template<range-with-movable-referencesforward_range V, size_t N>
   >         requires forward_range<V> && view<V> && (N > 0)
   >       template<bool Const>
   >       class adjacent_view<V, N>::iterator {
   >         […]
   >       };
   >     }

7. Modify 25.7.27.4 [range.adjacent.sentinel] as indicated:

   >     namespace std::ranges {
   >       template<range-with-movable-referencesforward_range V, size_t N>
   >         requires forward_range<V> && view<V> && (N > 0)
   >       template<bool Const>
   >       class adjacent_view<V, N>::sentinel {
   >         […]
   >       };
   >     }

8. Modify 25.7.33.2 [range.cartesian.view] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<range-with-movable-referencesinput_range First,
   >                range-with-movable-referencesforward_range... Vs>
   >         requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
   >       class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> {
   >         […]
   >       public:
   >         […]
   >         constexpr iterator<true> begin() const
   >           requires (range-with-movable-referencesrange<const First> && ... &&
   >                     range-with-movable-referencesrange<const Vs>);
   >         […]
   >         constexpr iterator<true> end() const
   >           requires (range-with-movable-references<const First> && ... &&
   >                     range-with-movable-references<const Vs>) &&
   >                    cartesian-product-is-common<const First, const Vs...>;
   >         […]
   >       };
   >     }

   > […]

   >     constexpr iterator<true> begin() const
   >       requires (range-with-movable-referencesrange<const First> && ... &&
   >                 range-with-movable-referencesrange<const Vs>);

   > -3- Effects: Equivalent to:

   > >     return iterator<true>(*this, tuple-transform(ranges::begin, bases_));

   >     constexpr iterator<false> end()
   >       requires ((!simple-view<First> || ... || !simple-view<Vs>)
   >         && cartesian-product-is-common<First, Vs...>);
   >     constexpr iterator<true> end() const
   >       requires (range-with-movable-references<const First> && ... && range-with-movable-references<const Vs>)
   >         && cartesian-product-is-common<const First, const Vs...>;

   > -4- Let:

   > > […]

9. Modify 25.7.33.3 [range.cartesian.iterator] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<range-with-movable-referencesinput_range First, range-with-movable-referencesforward_range... Vs>
   >         requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
   >       class cartesian_product_view<First, Vs...>::iterator {
   >         […]
   >       };
   >     }

### 3744(i). `copy_symlink(junction, new_symlink)`'s behavior is unclear

Section: 31.12.13.6 [fs.op.copy.symlink] Status: New Submitter: Nicole Mazzuca Opened: 2022-07-25 Last modified: 2022-08-23

Priority: 3

View all issues with New status.

Discussion:

The specification for `copy_symlink` is (31.12.13.6 [fs.op.copy.symlink]):

> Effects: Equivalent to `function(read_symlink(existing_symlink), new_symlink)` or `function(read_symlink(existing_symlink, ec), new_symlink, ec)`, respectively, where in each case `function` is `create_symlink` or `create_directory_symlink` as appropriate.

The specification for `read_symlink` is (31.12.13.29 [fs.op.read.symlink]):

> Returns: If `p` resolves to a symbolic link, a path object containing the contents of that symbolic link.

And finally, the definition of a "symbolic link" is (31.12.1 [fs.general]):

> A symbolic link is a type of file with the property that when the file is encountered during pathname resolution (31.12.6 [fs.class.path]), a string stored by the file is used to modify the pathname resolution.

On Unix, `symlink` is the only kind of symbolic link. However, on Windows, there are symbolic link files which are not `symlink`s (app execution aliases and junctions) — this means that `read_symlink` should almost certainly get the target of these files if possible. However, `copy_symlink` specifically requires creating a `symlink`, not whatever type of file was there originally. IMO, `copy_symlink` should require its target to be a symlink. I think the original assumption was that `read_symlink` would take care of that for `copy_symlink`; this is clearly not the case on Windows, though.

[2022-08-23; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3748(i). `common_iterator` and `counted_iterator`' `operator-` are missing cast to return type

Section: 24.5.5.6 [common.iter.cmp], 24.5.7.5 [counted.iter.nav] Status: New Submitter: Hewill Kang Opened: 2022-08-01 Last modified: 2022-08-23

Priority: 3

View all issues with New status.

Discussion:

Both `common_iterator` and `counted_iterator` explicitly specify that the return type of their `operator-` is `iter_difference_t<I2>`, however, given that the calculated type may be `iter_difference_t<I>`, we should do an explicit conversion here since the latter is not necessarily implicitly convertible to the former:

>     #include <ranges>

>     struct Y;

>     struct X {
>       X(Y);
>       using difference_type =
>     #ifdef __GNUC__
>       std::ranges::__detail::__max_diff_type;
>     #elif defined(_MSC_VER)
>       std::_Signed128;
>     #endif
>       int& operator*() const;
>       X& operator++();
>       void operator++(int);
>     };

>     struct Y {
>       using difference_type = std::ptrdiff_t;
>       int& operator*() const;
>       Y& operator++();
>       void operator++(int);
>     };

>     int main() {
>       std::counted_iterator<Y> y;
>       return std::counted_iterator<X>(y) - y; // hard error in stdlibc++ and MSVC-STL
>     }

Daniel:

> This issue shouldn't we voted until a decision for LWG 3749(i) has been made, because the first part of it overlaps with LWG 3749(i)'s second part.

[2022-08-23; Reflector poll]

Set priority to 3 after reflector poll.

"I think `common_iterator` should reject iterators with integer-class difference types since it can't possibly achieve the design intent of adapting them to Cpp17Iterators, so this issue should only affect `counted_iterator`."

"If the difference types of `I` and `I2` are different then the `operator-` can't be used to model `sized_sentinel_for`, since `i - i2` and `i2 - i` would have different types. Providing `operator-` under such circumstances seems to be of dubious value."

Proposed resolution:

This wording is relative to N4910.

1. Modify 24.5.5.6 [common.iter.cmp] as indicated:

   >     template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2>
   >       requires sized_sentinel_for<S, I2>
   >     friend constexpr iter_difference_t<I2> operator-(
   >       const common_iterator& x, const common_iterator<I2, S2>& y);

   > > -5- Preconditions: `x.v_.valueless_by_exception()` and `y.v_.valueless_by_exception()` are each `false`.

   > > -6- Returns: `0` if `i` and `j` are each `1`, and otherwise `static_cast<iter_difference_t<I2>>(``get<i>(x.v_) - get<j>(y.v_)``)`, where `i` is `x.v_.index()` and `j` is `y.v_.index()`.

2. Modify 24.5.7.5 [counted.iter.nav] as indicated:

   >     template<common_with<I> I2>
   >       friend constexpr iter_difference_t<I2> operator-(
   >         const counted_iterator& x, const counted_iterator<I2>& y);

   > > -13- Preconditions: `x` and `y` refer to elements of the same sequence (24.5.7.1 [counted.iterator]).

   > > -14- Effects: Equivalent to: `return static_cast<iter_difference_t<I2>>(y.length - x.length);`

### 3758(i). Element-relocating operations of `std::vector` and `std::deque` should conditionally require Cpp17CopyInsertable in their preconditions

Section: 23.3.13.3 [vector.capacity], 23.3.13.5 [vector.modifiers], 23.3.5.3 [deque.capacity], 23.3.5.4 [deque.modifiers] Status: New Submitter: Jiang An Opened: 2022-08-24 Last modified: 2022-11-06

Priority: 3

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with New status.

Discussion:

This issue is raised from editorial issue #5776.

In order to achieve strong exception safety, some operations of `std:vector` and `std::deque` may use copy insertion for relocation of old elements, if move construction of its element type is potentially throwing and copy insertion is available. However, currently only Cpp17MoveInsertable is mentioned in many of their Preconditions (e.g. those of `insert` for rvalues), which seemly fails to cover the cases in which copy insertion is formally invalid but the semantic requirements of Cpp17CopyInsertable are not met.

Perhaps we should create a new named requirement for these operations, which is equivalent to Cpp17CopyInsertable when `!is_nothrow_move_constructible_v<T> && is_copy_constructible_v<T>` is `true`, and equivalent to Cpp17MoveInsertable otherwise.

[2022-09-23; Reflector poll]

Set priority to 3 after reflector poll.

Jonathan: I think the point (which LWG 3758 fails to explain clearly) is that today's implementations sometimes use copy insertion when move insertion is syntactically valid, but is potentially-throwing. But the preconditions don't require copy insertion. If `vector::resize(size_type)` decides to use copy construction, because move construction might throw and the type is copy constructible (which is implied to be permitted by the Remarks), do we require Cpp17CopyInsertable's semantic requirement that the new value is equivalent to the one we copied? We don't say so.
tl;dr The user is trying to resize a vector and the value type is Cpp17MoveInsertable into the vector, but the implementation decides to copy not move. What are the preconditions on the user's type?

[2022-11-06; Daniel comments]

This issue has considerable overlap with LWG 2158(i).

Proposed resolution:

### 3763(i). Should range adaptor iterators only provide `iterator_category` when its `difference_type` is not an integer-class type?

Section: 25.6.5.3 [range.repeat.iterator], 25.7.33.3 [range.cartesian.iterator] Status: New Submitter: Hewill Kang Opened: 2022-08-27 Last modified: 2023-02-07

Priority: 3

View all issues with New status.

Discussion:

After P2259, the range adaptor' iterators only provide `iterator_category` member when the underlying range models `forward_range`, which is mainly based on the premise that all valid C++20 forward iterators meet the C++17 input iterator requirements.

However, this is not strictly correct. When the underlying range's `difference_type` is an integer-class type, its iterator does not conform Cpp17InputIterator.

Although `iterator_traits<I>::iterator_category` will still deduce the correct category in this case since these iterators have no `reference` member, it might be misleading to provide these incorrect member types.

Do we need to aggressively prohibit these iterators from providing `iterator_category` when their difference type is an integer-class type?

The proposed resolution makes `repeat_view::iterator` conditionally provide `iterator_category`, because it explicitly mentions `IOTA-DIFF-T(index-type)` in the definition of `difference_type`, which makes it consistent with LWG 3670(i).

It also removes the `reference` member type of `cartesian_product_view::iterator`, which prevents `iterator_traits<I>::iterator_category` from being aliased to its member `iterator_category`, so that `iterator_traits<I>::iterator_category` will not always be an `input_iterator_tag` when its `difference_type` is an integer-class type.

This is also consistent with other range adaptors, as none of them have a `reference` member type.

[2022-09-23; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.6.5.3 [range.repeat.iterator] as indicated:

   >     namespace std::ranges {
   >       template<move_constructible W, semiregular Bound = unreachable_sentinel_t>
   >         requires (is_object_v<W> && same_as<W, remove_cv_t<W>> &&
   >                   (is-integer-like<Bound> || same_as<Bound, unreachable_sentinel_t>))
   >       class repeat_view<W, Bound>::iterator {
   >       private:
   >         using index-type =                  // exposition only
   >           conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>;
   >         const W* value_ = nullptr;          // exposition only
   >         index-type current_ = index-type(); // exposition only

   >         constexpr explicit iterator(const W* value, index-type b = index-type());   // exposition only

   >       public:
   >         using iterator_concept = random_access_iterator_tag;
   >         using iterator_category = random_access_iterator_tag;           // present only if difference_type
   >                                                                         // is an integral type
   >         using value_type = W;
   >         using difference_type = conditional_t<is-signed-integer-like<index-type>,
   >             index-type,
   >             IOTA-DIFF-T(index-type)>;
   >         […]
   >       };
   >     }

2. Modify 25.7.33.3 [range.cartesian.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range First, forward_range... Vs>
   > >         requires (view<First> && ... && view<Vs>)
   > >       template<bool Const>
   > >       class cartesian_product_view<First, Vs...>::iterator {
   > >       public:
   > >         using iterator_category = input_iterator_tag;
   > >         using iterator_concept  = see below;
   > >         using value_type = tuple<range_value_t<maybe-const<Const, First>>,
   > >           range_value_t<maybe-const<Const, Vs>>...>;
   > >         using reference = tuple<range_reference_t<maybe-const<Const, First>>,
   > >           range_reference_t<maybe-const<Const, Vs>>...>;
   > >         using difference_type = see below;
   > >         […]

   > >         constexpr autoreference operator[](difference_type n) const
   > >           requires cartesian-product-is-random-access<Const, First, Vs...>;

   > >         […]
   > >       };
   > >     }

   > […]

   >     constexpr autoreference operator[](difference_type n) const
   >       requires cartesian-product-is-random-access<Const, First, Vs...>;

   > > -24- Effects: `return *((*this) + n);`

### 3783(i). `views::common` may not be a range adaptor object

Section: 24.5.5.1 [common.iterator], 24.5.5.6 [common.iter.cmp] Status: New Submitter: Hewill Kang Opened: 2022-09-18 Last modified: 2022-10-12

Priority: 3

View other active issues in [common.iterator].

View all other issues in [common.iterator].

View all issues with New status.

Discussion:

P2325R3 makes `input_or_output_iterator` no longer require `default_initializable`, which means that the template parameter `I` of `common_iterator` no longer requires `default_initializable`.

In this case, since `common_iterator` itself cannot be default-constructed, it can never be a valid sentinel even if it can be compared to itself. Furthermore, this also makes `views::common` return a non-`range` even if it is well-formed (online example):

>     #include <ranges>
>     #include <vector>

>     int main() {
>       std::vector<int> v;
>       auto r = std::views::counted(std::back_inserter(v), 3);
>       auto cr = r | std::views::common;
>       static_assert(std::ranges::range<decltype(cr)>); // failed
>     }

which causes `views::common` to be unable to convert a `range` into a `view`, making it not a valid range adaptor.

I think `common_iterator` should always be `default_initializable`, which makes it eligible to be a legitimate sentinel.

The proposed resolution provides a default constructor for `common_iterator` when `I` is not `default_initializable`, in which case constructs the `variant` with an alternative type of `S`.

[2022-09-28; Reflector poll]

Set priority to 3 after reflector poll.

"The P/R means that sometimes the variant containers an iterator and sometimes contains a sentinel, depending on whether the iterator is default constructible. Always constructing a sentinel would be more consistent."

Proposed resolution:

This wording is relative to N4917.

1. Modify 24.5.5.1 [common.iterator], class template `common_iterator` synopsis, as indicated:

   >     namespace std {
   >       template<input_or_output_iterator I, sentinel_for<I> S>
   >         requires (!same_as<I, S> && copyable<I>)
   >       class common_iterator {
   >       public:
   >         constexpr common_iterator() requires default_initializable<I> = default;
   >         constexpr common_iterator();
   >         […]
   >       };
   >       […]
   >     }

2. Modify 24.5.5.3 [common.iter.const] as indicated:

   >     constexpr common_iterator();

   > > -?- Effects: Initializes `v_` as if by `v_{in_place_type<S>}`.

   >     constexpr common_iterator(I i);

   > > -1- Effects: Initializes `v_` as if by `v_{in_place_type<I>, std::move(i)}`.

### 3793(i). Requirements for some algorithms' `Size` template parameters are unclear

Section: 26.6.5 [alg.foreach], 26.6.15 [alg.search], 26.7.1 [alg.copy], 26.7.6 [alg.fill], 26.7.7 [alg.generate] Status: New Submitter: Jiang An Opened: 2022-10-05 Last modified: 2022-10-12

Priority: 3

View other active issues in [alg.foreach].

View all other issues in [alg.foreach].

View all issues with New status.

Discussion:

Algorithms `std::for_each_n`, `std::search_n`, `std::copy_n`, `std::fill_n`, and `std::generate_n` have similar requirements for the `Size` template parameter in Mandates, requiring `Size` to be convertible to an integral type.

However, it is currently underspecified to which integral type `Size` is converted before further operations. There is implementation divergence:

> - libstdc++ and libc++ use an overload set to determine the target type, and

> - MSVC STL simply chooses `std::ptrdiff_t` when `Size` is not an integral type.

It is also notable that when the conversion from the source type to integral types is sufficiently ambiguous, none of these implementations accepts such a source type.

For example, currently the following program is rejected by all mainstream implementations.

>     #include <algorithm>
>     #include <cstdio>

>     struct BadFrom {
>       operator short() const { return 1; }
>       operator unsigned short() const { return 1; }
>     };

>     int main()
>     {
>       int arr[42]{};
>       std::for_each_n(arr, BadFrom{}, [&arr](int i)
>       {
>         std::printf("%d\n", i);
>       });
>     }

I think libc++'s strategy make the most sense. But is it really intended to support using a floating-point type or a class type as `Size`?

Daniel:

The conversion from class type was indeed intended, see the original wording for LWG 3213(i), which was transferred to P1718R2.

See also LWG 3439(i) for a similar underspecified situation for template parameter `Distance` and for the underspecified `Size` template parameter in various `uninitialized_*_n` and `destroy_n` algorithms in 26.11.2 [special.mem.concepts].

`ranges::destroy_n` has the luxury to simply require `iter_difference_t<I>`.

[2022-10-12; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3794(i). `std::filesystem::path::iterator::reference` should be allowed to be `std::filesystem::path`

Section: 31.12.6.6 [fs.path.itr] Status: New Submitter: Jiang An Opened: 2022-10-17 Last modified: 2022-11-01

Priority: 3

View all other issues in [fs.path.itr].

View all issues with New status.

Discussion:

Currently, 31.12.6.6 [fs.path.itr]/2 effectively requires `std::filesystem::path::iterator::reference` to be a reference type, due to the requirements for (legacy) bidirectional iterators. However, it's reasonable for the `operator*` to return `path` by value, which can make the iterator model `std::bidirectional_iterator`, be compatible with `std::reverse_iterator`, and avoid complicated data structures (e.g. those in libstdc++) for achieving such purpose.

libc++ is already doing so. I think we should make such a strategy conforming.

[2022-11-01; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4917.

1. Modify 31.12.6.6 [fs.path.itr] as indicated:

   > -2- A `path::iterator` is a constant iterator meeting all the requirements of a bidirectional iterator (24.3.5.6 [bidirectional.iterators]) except that, for dereferenceable iterators `a` and `b` of type `path::iterator` with `a == b`, there is no requirement that `*a` and `*b` are bound to the same object, and its `reference` may be `path`. Its `value_type` is `path`.

### 3799(i). Should `<math.h>` provide 3-argument `::hypot` overloads?

Section: 17.15.7 [support.c.headers.other] Status: New Submitter: Jiang An Opened: 2022-10-22 Last modified: 2022-11-01

Priority: 3

View other active issues in [support.c.headers.other].

View all other issues in [support.c.headers.other].

View all issues with New status.

Discussion:

See also LWG 3782(i). Like `lerp`, neither `<math.h>` nor C compatibility is mentioned in P0030R1, and MSVC STL decides not to declare 3-argument `hypot` overloads in the global namespace (perhaps so does libc++).

Perhaps we should also avoid providing these overloads in the global namespace. However, such change seems a bit difficult for libstdc++'s `<math.h>`.

[2022-11-01; Reflector poll]

Set priority to 3 after reflector poll. "This affects the exports of `std.compat`".

Proposed resolution:

### 3802(i). `flat_foo` allocator-extended constructors lack move semantics

Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: New Submitter: Arthur O'Dwyer Opened: 2022-10-25 Last modified: 2023-06-16

Priority: 2

View other active issues in [flat.map].

View all other issues in [flat.map].

View all issues with New status.

Discussion:

Compare 23.6.4.2 [priqueue.cons]'s overload set

>     priority_queue(const Compare&, const Container&);
>     priority_queue(const Compare&, Container&&);
>     template<class Alloc> priority_queue(const Compare&, const Container&, const Alloc&);
>     template<class Alloc> priority_queue(const Compare&, Container&&, const Alloc&);

against 23.6.8 [flat.map]'s overload set

>     flat_map(key_container_type, mapped_container_type);
>     template<class Allocator> flat_map(const key_container_type&, const mapped_container_type&, const Allocator& a);

I see two issues here:

1. (A) The allocator-extended ctor of `flat_map` always copies the `key_container` and `value_container`, when it should be move-enabled.

2. (B) Almost certainly the `Allocator` parameter should be named `Alloc` instead, and there should be a separate "Constructors with allocators" section with wording similar to 23.6.4.3 [priqueue.cons.alloc] explaining that these ctors don't participate in overload resolution unless `uses_allocator_v<KeyContainer, Alloc> && uses_allocator_v<MappedContainer, Alloc>`.

I suggest this overload set to replace the two overloads above:

>     flat_map(key_container_type, mapped_container_type);
>     template<class Alloc> flat_map(const key_container_type&, const mapped_container_type&, const Alloc& a);
>     template<class Alloc> flat_map(const key_container_type&, mapped_container_type&&, const Alloc& a);
>     template<class Alloc> flat_map(key_container_type&&, const mapped_container_type&, const Alloc& a);
>     template<class Alloc> flat_map(key_container_type&&, mapped_container_type&&, const Alloc& a);

This preserves the apparent assumption that `KeyContainer(std::move(kc))` is always efficient but `KeyContainer(std::move(kc), otheralloc)` might not be. Similar wording changes would have to be made to all the `flat_foo` containers.

Tony Table:

>     template<class T, class Comp = std::less<T>, class Container = std::pmr::vector<T>>
>     using pmr_flat_set = std::flat_set<T, Comp, Container>;

>     std::pmr::vector<pmr_flat_set<int>> vs;
>     std::pmr::vector<int> data = {1,2,3};

>     vs.reserve(1);
>     vs.emplace_back(std::move(data));
>       // constructs-in-place with the argument list (std::move(data), get_allocator())
>       // BEFORE: copies (causes heap traffic)
>       // AFTER: moves (no heap traffic)

[2022-11-04; Reflector poll]

Set priority to 2 after reflector poll.

[2023-06-14 Varna]

Mentioned in P2767R0, but not resolved by it.

Proposed resolution:

### 3804(i). `flat_foo` missing some allocator-extended deduction guides

Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: New Submitter: Arthur O'Dwyer Opened: 2022-10-25 Last modified: 2022-11-04

Priority: 2

View other active issues in [flat.map].

View all other issues in [flat.map].

View all issues with New status.

Discussion:

Tony Table:

>     std::vector<int> v;
>     std::flat_set s = std::flat_set(v, MyAllocator<int>());
>     std::flat_set s = std::flat_set(v, std::less(), MyAllocator<int>());
>     std::flat_set s = std::flat_set(v.begin(), v.end(), MyAllocator<int>());
>     std::flat_set s = std::flat_set(v.begin(), v.end(), std::less(), MyAllocator<int>());
>     // BEFORE: all fail to compile
>     // AFTER: all compile successfully

Contrast 23.6.11.3 [flat.set.cons] with 23.6.4.2 [priqueue.cons], where most of these are okay:

>     std::vector<int, MyAllocator<int>> v;
>     std::priority_queue pq1 = std::priority_queue(v, std::less(), MyAllocator<int>());
>     std::priority_queue pq2 = std::priority_queue(v.begin(), v.end(), MyAllocator<int>());
>     std::priority_queue pq3 = std::priority_queue(v.begin(), v.end(), std::less(), MyAllocator<int>());
>     // BEFORE AND AFTER: pq1 compiles successfully
>     // BEFORE AND AFTER: pq2 and pq3 also compile successfully thanks to LWG 3506(i)

[2022-11-04; Reflector poll]

Set priority to 2 after reflector poll.

Proposed resolution:

### 3805(i). Expression evaluating to a call wrapper is a prvalue, not an object

Section: 22.10 [function.objects], 25.5.7.3 [range.utility.conv.adaptors], 25.7.2 [range.adaptor.object] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2022-10-26 Last modified: 2022-11-01

Priority: 4

View all other issues in [function.objects].

View all issues with New status.

Discussion:

22.10.3 [func.def] defines

> -6- A call wrapper is an object of a call wrapper type.

Most importantly, a call wrapper is an object.

A number of functions in 22.10 [function.objects] and expressions in 25.5.7.3 [range.utility.conv.adaptors] and 25.7.2 [range.adaptor.object] are specified to result in a call wrapper. Most notably, the return type of `ranges::to` is `auto`, so its result is definitely a prvalue and not a "call wrapper" object.

Where a prvalue result is meant, the wording should be clarified to mean "a prvalue whose object it initializes is a call wrapper".

[2022-11-01; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

### 3812(i). [fund.ts.v3] Incorrect constraint on `propagate_const` conversion function

Section: 6.1.2.6 [fund.ts.v3::propagate_const.const_observers] Status: New Submitter: Giuseppe D'Angelo Opened: 2022-11-04 Last modified: 2022-11-12

Priority: 3

View all issues with New status.

Discussion:

Addresses: fund.ts.v3

This issue has its origin in the discussion of gcc issue 107525.

The current draft of LFTSv3 specifies this conversion function for `propagate_const` in 6.1.2.6 [fund.ts.v3::propagate_const.const_observers]:

>     constexpr operator const element_type*() const;

> > -7- Returns: `get()`.

> > -8- Remarks: This function shall not participate in overload resolution unless `T` is an object pointer type or has an implicit conversion to `const element_type*`.

The constraint should however specify that `const T` (and not `T`) needs to have an implicit conversion to `const element_type *`.

Basically: if `const T` cannot do the conversion, then neither `const propagate_const<T>` should be able to.

One can design a type `X` such as a `const X` cannot convert to `const element_type *` (for instance, by `=delete`ing the corresponding conversion function). If now one asks whether `const propagate_const<X>` is convertible to `const element_type *`, the answer is (surprisingly) "yes".

[Kona 2022-11-12; Set priority to 3]

Proposed resolution:

This wording is relative to N4840.

1. Modify 6.1.2.6 [fund.ts.v3::propagate_const.const_observers] as indicated:

   >     constexpr operator const element_type*() const;

   > > -7- Returns: `get()`.

   > > -8- Remarks: This function shall not participate in overload resolution unless `T` is an object pointer type or `const T` has an implicit conversion to `const element_type*`.

### 3813(i). `std::span<volatile T, E>` is made ill-formed by P2278R4 when `T` is a normal class type

Section: 23.7.2.2.1 [span.overview] Status: New Submitter: Jiang An Opened: 2022-11-06 Last modified: 2025-03-22

Priority: 2

View all other issues in [span.overview].

View all issues with New status.

Discussion:

This issue is discovered when implementing the `span` part of P2278R4 in MSVC STL.

P2278R4 added the `const_iterator` member type to `std::span`, which required its `iterator` type to model `std::input_iterator` (same for `const_reverse_iterator` and `reverse_iterator`).

However, when `element_type` is `volatile T` and `T` is a class type, the `iterator` type generally fails to satisfy `input_iterator`, because:

1. `input_iterator<iterator>` requires `indirectly_readable<iterator>`;

2. `indirectly_readable<iterator>` requires `common_reference_with<iterator_reference_t<iterator>&&, iterator_rvalue_reference_t<iterator>&&>`, that is `common_reference_with<volatile T&, volatile T&&>`;

3. `common_reference_t<volatile T&, volatile T&&>` is `T` (which is problematic), and thus `common_reference_with<volatile T&, volatile T&&>` requires both `convertible_to<volatile T&, T>` and `convertible_to<volatile T&&, T>`;

4. However, the class type `T` generally doesn't have constructors from `volatile T` or `const volatile T` glvalues, and thus neither `convertible_to<volatile T&, T>` and `convertible_to<volatile T&&, T>` is satisfied.

Ideally, `span` should not require any form of construction of `element_type`. Although usages of class types provided by the standard library via `volatile` glvalues are generally not supported, I think `span<volatile T, E>` is still useful for some user-defined class type `T`, and thus shouldn't be forbidden.

[Kona 2022-11-12; Set priority to 2]

[2025-03-22; Jiang An comments and provides wording]

The proposed resolution relaxes the `input_iterator` concept (which in turn relaxes stronger iterator concepts) and extends `iter_const_reference_t` and the exposition only `iter-const-rvalue-reference-t` aliases. Another approach can be just relaxing the `indirectly_readable` concept, but its impact may be larger than expected.

Proposed resolution:

This wording is relative to N5008.

1. Modify 24.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     […]
   >     // 24.3.4.9 [iterator.concept.input], concept input_iterator
   >     template<class I>
   >       concept deref-to-value-t = see below;            // freestanding

   >     template<class I>
   >       concept weakly-indirectly-readable = see below;  // freestanding

   >     template<class I>
   >     concept input_iterator = see below;                // freestanding

   >     […]

   >     // 24.5.3.2 [const.iterators.alias], alias templates
   >     template<indirectly_readableweakly-indirectly-readable I>
   >       using iter_const_reference_t = see below;

2. Modify 24.3.4.9 [iterator.concept.input], concept `input_iterator` synopsis, as indicated:

   >     template<class I>
   >       concept deref-to-value-t-impl =      // exposition only
   >         same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>> &&
   >         is_object_v<iter_value_t<I>>;

   >     template<class I>
   >       concept deref-to-value-t =           // exposition only
   >         deref-to-value-t-impl<remove_cvref_t<I>>;

   >     template<class I>
   >       concept weakly-indirectly-readable = // exposition only
   >         deref-to-value-t<I> ||
   >         indirectly_readable<I>;

   >     template<class I>
   >       concept input_iterator =
   >         input_or_output_iterator<I> &&
   >         indirectly_readableweakly-indirectly-readable<I> &&
   >         requires { typename ITER_CONCEPT(I); } &&
   >         derived_from<ITER_CONCEPT(I), input_iterator_tag>;

3. Modify 24.5.3.2 [const.iterators.alias] as indicated:

   >     template<class T>
   >       using ref-add-const-t = see below;

   > > -?- Result: If `is_lvalue_reference_v<T>` is `true`, `const remove_reference_t<T>&`. Otherwise, if `is_rvalue_reference_v<T>` is `true`, `const remove_reference_t<T>&&`. Otherwise, `T`.

   >     template<indirectly_readableweakly-indirectly-readable It>
   >       using iter_const_reference_t = see below;
   >         common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>;

   > > -?- Result: If `It` models `deref-to-value-t`, `ref-add-const-t<iter_reference_t<It>>`. Otherwise, `common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>`.

   > [Drafting note: The simple `deref-to-value-t` case should be detected first, which avoids unnecessary instantiations and IFNDR-ness.]

4. Modify 24.5.3.3 [const.iterators.iterator] as indicated:

   >     […]
   >     template<indirectly_readableweakly-indirectly-readable I>
   >       using iter-const-rvalue-reference-t =              // exposition only
   >         common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>>;
   >     […]

### 3829(i). `as_rvalue_view::end` should improve non-common case

Section: 25.7.7.2 [range.as.rvalue.view] Status: New Submitter: Hewill Kang Opened: 2022-11-13 Last modified: 2022-11-30

Priority: 3

View all issues with New status.

Discussion:

Currently, when the underlying range is not a common range, `as_rvalue_view::begin` and `end` return `move_iterator` and `move_sentinel` respectively, this seems reasonable since `move_sentinel` is a sentinel adaptor introduced by C++20 specifically for comparison with `move_iterator`.

However, in the case where the sentinel type of the underlying type is an input iterator, this may lead to some performance issues, consider:

>     #include <list>
>     #include <ranges>

>     int main() {
>       std::list<std::tuple<int&>> l;
>       auto k = std::move(l) | std::views::keys;
>       auto s = std::ranges::subrange(std::cbegin(k), std::end(k));
>       (void) std::ranges::next(s.begin(), s.end()); // constant time
>       auto r = s | std::views::as_rvalue;
>       (void) std::ranges::next(r.begin(), r.end()); // linear time
>     }

The above `subrange` is constructed by the `elements_view::iterator<true>` and `elements_view::iterator<false>` pair, and since the former can be assigned by the latter, when we use `ranges::next` to increment the `begin` of `s` to its `end`, the `assignable_from` branch will be executed, so we get a constant-time complexity.

However, when we apply `views::as_rvalue` to `s`, the `as_rvalue_view::end` will go into the non-common branch and return `move_sentinel`. And because `move_iterator` cannot be assigned by `move_sentinel`, `ranges::next` will successively increment the `begin` of `s` until its end, we get the linear-time complexity this time.

I think it is more appropriate to return `move_iterator` for the above case, as this preserves the assignability, but also preserves the iterator operability that the original sentinel type has.

Another benefit of doing this is that when the sentinel type of underlying range can be subtracted from its iterator type but does not model `sized_sentinel_for`, returning different `move_iterator` makes them still subtractable, because its `operator-` only constrain the `x.base() - y.base()` being well-formed.

This also solves the issue of `as_rvalue_view` being a valid type but does not model a `range` in some cases, for example:

>     #include <ranges>

>     int main() {
>       std::move_iterator<int*> i;
>       std::move_iterator<const int*> ci;
>       std::ranges::subrange s(i, ci);
>       std::ranges::as_rvalue_view r(s); // not failed
>       static_assert(std::ranges::range<decltype(r)>); // failed
>     }

This is because currently, `as_rvalue_view` does not explicitly specify the template parameters of the returned `move_iterator` and `move_sentinel`, so based on CTAD, its `begin` will return `move_iterator(move_iterator(...))` which is still `move_iterator<int*>`, and its `end` will return `move_sentinel<move_iterator<const int*>>`. Those two types are not comparable, so `r` does not constitute a valid `range`.

The proposed resolution is to return `move_iterator` when the sentinel type of the underlying range models `input_iterator`.

[2022-11-30; Reflector poll]

Set priority to 3 after reflector poll.

"NAD, these examples seem entirely contrived. If not NAD, don't need the `common_range` check if we are checking that the sentinel models `input_iterator`."

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.7.7.2 [range.as.rvalue.view] as indicated:

   >     namespace std::ranges {
   >       template<view V>
   >         requires input_range<V>
   >       class as_rvalue_view : public view_interface<as_rvalue_view<V>> {
   >         […]
   >         constexpr auto begin() requires (!simple-view<V>)
   >         { return move_iterator(ranges::begin(base_)); }
   >         constexpr auto begin() const requires range<const V>
   >         { return move_iterator(ranges::begin(base_)); }

   >         constexpr auto end() requires (!simple-view<V>) {
   >           if constexpr (common_range<V> || input_iterator<sentinel_t<V>>) {
   >             return move_iterator(ranges::end(base_));
   >           } else {
   >             return move_sentinel(ranges::end(base_));
   >           }
   >         }
   >         constexpr auto end() const requires range<const V> {
   >           if constexpr (common_range<const V> || input_iterator<sentinel_t<const V>>) {
   >             return move_iterator(ranges::end(base_));
   >           } else {
   >             return move_sentinel(ranges::end(base_));
   >           }
   >         }
   >         […]
   >       };
   >       […]
   >     }

### 3830(i). `reverse_view` should not cache when `ranges::next` has constant time complexity

Section: 25.7.21.2 [range.reverse.view] Status: New Submitter: Hewill Kang Opened: 2022-11-14 Last modified: 2022-11-30

Priority: 3

View other active issues in [range.reverse.view].

View all other issues in [range.reverse.view].

View all issues with New status.

Discussion:

In order to ensure that `begin` has an amortized constant time, when the underlying range is not a common range, `reverse_view` always caches the result of `ranges::next`.

However, for some non-common ranges, incrementing its `begin` to `end` still guarantees constant time, for example:

>     #include <ranges>
>     #include <vector>
>     #include <list>

>     int main() {
>       std::vector v{42};
>       auto x = std::ranges::subrange(std::counted_iterator(v.begin(), 1), std::default_sentinel)
>              | std::views::reverse;
>       (void) x.begin(); // still caches end iterator in MSVC-STL

>       std::list l{42};
>       auto y = std::ranges::subrange(l.cbegin(), l.end())
>              | std::views::reverse;
>       (void) y.begin(); // still caches end iterator in both libstdc++ and MSVC-STL
>     }

In the above example, although neither `subrange` is a common range, applying `ranges::next` to their iterator-sentinel pairs is still constant time, in this case, there's no need to introduce a cache for `reverse_view` to store the results. We shouldn't pay for things we don't need to use.

[2022-11-30; Reflector poll]

Set priority to 3 after reflector poll.

"NAD as specified, the Remarks don't need to be precise, they already allow caching to be omitted if not needed. But we could still enable const overloads of `begin` for cases like these."

"NAD, no need to complicate constraints for such contrived examples. We don't care about random access, sized, non-common ranges like the first case. Any change here should be a paper covering all adaptors, not a piecemeal issue."

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.7.21.2 [range.reverse.view] as indicated:

   >     constexpr reverse_iterator<iterator_t<V>> begin();

   > > -2- Returns:

   > > >     make_reverse_iterator(ranges::next(ranges::begin(base_), ranges::end(base_)))

   > > -3- Remarks: In order to provide the amortized constant time complexity required by the `range` concept, this function caches the result within the `reverse_view` for use on subsequent calls when both `assignable_from<I&, S>` and `random_access_iterator<I> && sized_sentinel_for<S, I>` are `false`, where `I` is `iterator_t<V>` and `S` is `sentinel_t<V>`.

### 3832(i). Missing change for `element_view::iterator` in LWG 3798

Section: 25.7.23.3 [range.elements.iterator] Status: New Submitter: Jiang An Opened: 2022-11-23 Last modified: 2024-07-21

Priority: 3

View other active issues in [range.elements.iterator].

View all other issues in [range.elements.iterator].

View all issues with New status.

Discussion:

In LWG 3798(i) (voted into WP in November 2022), iterator types of several range adaptors may have `forward_iterator_tag` or stronger iterator tag types as their `iterator_category` type when their `operator*` returns rvalue references. However, the proposed resolution missed the similar change for `element_view::iterator`.

[2022-11-30; Reflector poll]

Set priority to 3 after reflector poll.

"The proposed resolution is incorrect - just because the `get` expression is an xvalue doesn't mean `operator*` returns by reference."

Previous resolution [SUPERSEDED]:

> This wording is relative to N4917.

> 1. Modify 25.7.23.3 [range.elements.iterator] as indicated:

>    > -2- The member typedef-name `iterator_category` is defined if and only if `Base` models `forward_range`. In that case, `iterator_category` is defined as follows: Let `C` denote the type `iterator_traits<iterator_t<Base>>::iterator_category`.

>    > 1. (2.1) — If `std::get<N>(*current_)` is an prvalue, `iterator_category` denotes `input_iterator_tag`.

>    > 2. (2.2) — Otherwise, if `C` models `derived_from<random_access_iterator_tag>`, `iterator_category` denotes `random_access_iterator_tag`.

>    > 3. (2.3) — Otherwise, `iterator_category` denotes `C`.

[2023-01-22; Jiang An comments and provides improved wording]

The old proposed resolution was incorrect. I think the correct criteria could be that determined from the return type of `get-element`.

Previous resolution [SUPERSEDED]

This wording is relative to N4928.

1. Modify 25.7.23.3 [range.elements.iterator] as indicated:

   > -2- The member typedef-name `iterator_category` is defined if and only if `Base` models `forward_range`. In that case, `iterator_category` is defined as follows: Let `C` denote the type `iterator_traits<iterator_t<Base>>::iterator_category`.

   > 1. (2.1) — If `range_reference_t<Base>` is a reference type and `std::get<N>(*current_)` is an prvalue or neither `range_reference_t<Base>` nor `tuple_element_t<N, range_reference_t<Base>` is a reference type, `iterator_category` denotes `input_iterator_tag`.

   > 2. (2.2) — Otherwise, if `C` models `derived_from<random_access_iterator_tag>`, `iterator_category` denotes `random_access_iterator_tag`.

   > 3. (2.3) — Otherwise, `iterator_category` denotes `C`.

[2024-07-08; Hewill Kang provides improved wording]

Proposed resolution:

This wording is relative to N4986.

1. Modify 25.7.23.3 [range.elements.iterator] as indicated:

   > -2- The member typedef-name `iterator_category` is defined if and only if `Base` models `forward_range`. In that case, `iterator_category` is defined as follows: Let `C` denote the type `iterator_traits<iterator_t<Base>>::iterator_category`.

   > 1. (2.1) — If `std::get<N>(*current_)``is_reference_v<decltype(get-element(current_))>` is an rvalue `false`, `iterator_category` denotes `input_iterator_tag`.

   > 2. (2.2) — Otherwise, if `C` models `derived_from<random_access_iterator_tag>`, `iterator_category` denotes `random_access_iterator_tag`.

   > 3. (2.3) — Otherwise, `iterator_category` denotes `C`.

### 3835(i). Requirements for `CharT` in the regex library

Section: 28.6.1 [re.general] Status: New Submitter: Xie He Opened: 2022-11-28 Last modified: 2023-01-06

Priority: 4

View all issues with New status.

Discussion:

In 28.6.1 [re.general], the character type used in the regex library (`CharT`), is only required to be "char-like". This means "`struct A { int m; };`" satisfies the requirements for `CharT`. Clearly there have to be more requirements for `CharT`.

1. Comparison operators such as "`==`" or "`<=`" must be defined between `CharT` objects, because these operators are used at various places of 28.6 [re], including 28.6.2 [re.req] paragraph 14 and 16, 28.6.6 [re.traits] paragraph 12, and 28.6.12 [re.grammar] paragraph 14. Similarly, "`==`" between a `CharT` object and integer 0 must also be defined, as it is used at 28.6.2 [re.req] paragraph 11.

   Alternatively, we can also interpret these operators in the text, as code implemented using "`std::char_traits<CharT>`" comparison functions. This interpretation has the advantage of keeping 28.6 [re] consistent with the strings library and string comparisons, which are also used in 28.6 [re], including 28.6.2 [re.req] paragraph 7, 18, 20, and 28.6.12 [re.grammar] paragraph 14.2. Also, only with this interpretation, can 28.6.6 [re.traits] paragraph 3 be consistent with 28.6.2 [re.req] paragraph 11.

2. There must be a way to convert between `CharT` and `char`, otherwise there is no way to recognize regex syntactical characters, such as `'*'` or `'+'`. One way is to simply do a type conversion from `char` to `CharT` (which requires `char` to be convertible to `CharT`). But this doesn't allow us to convert between character encodings.

   Alternatively, if we have an object of the `Traits` class (28.6.12 [re.grammar] paragraph 2) denoted `v`, we can use "`use_facet<ctype<CharT>>(v.getloc()).widen`" to convert from `char` to `CharT`. This allows us to convert between character encodings. This requires that we can actually get this facet from this locale object.

[2023-01-06; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

### 3837(i). `std::erase_if` overloads for non-associative containers should move (and not copy) their predicate object

Section: 27.4.4.5 [string.erasure], 23.3.5.5 [deque.erasure], 23.3.7.7 [forward.list.erasure], 23.3.11.6 [list.erasure], 23.3.13.6 [vector.erasure] Status: New Submitter: Giuseppe D'Angelo Opened: 2022-12-04 Last modified: 2023-01-06

Priority: 3

View all issues with New status.

Discussion:

Consistent uniform erasure added several overloads of `std::erase_if`. All these overloads have fully specified effects, and are either implemented through the `erase`/`remove_if` idiom (for `string`, `vector` and `deque`), through calls to the container's own `remove_if` non-static member function (for `list` and `forward_list`), or through a hand-rolled loop (for the associative containers — `map`, `set`, and their unordered and multi variants).

For the overloads that deal with non-associative containers the predicate passed to `std::erase_if` is always copied into the inner call to `std::remove_if` or the container's `remove_if` (see 27.4.4.5 [string.erasure], 23.3.13.6 [vector.erasure], 23.3.11.6 [list.erasure], 23.3.7.7 [forward.list.erasure], and 23.3.5.5 [deque.erasure]).

Now, algorithms are generally allowed to take as many copies of predicates as they want — this is 26.2 [algorithms.requirements]/9, but cf. LWG 3049(i). However it still feels strange/sloppy that a copy here is mandated by the Standard. An implementation that would otherwise make no copies of a predicate in an algorithm must make a copy in the `erase_if` overloads.

The copy of the predicate could be instead replaced by a move without any change in functionality; after being passed to the underlying algorithm, the predicate object is never used again by `erase_if`. I am therefore proposing to add a `std::move` call for the predicate object.

One could argue that `erase_if` should be re-specified so that it is not necessarily implemented in terms of underlying calls to other facilities, and could therefore even remove the need of a move on a high-quality implementation. This is a design change, and so I consider it out of scope for a library issue.

Of course, moving instead of copying is a detectable change, and "pathological" predicate types that are copyable but have disabled moves will get broken, but I don't think those deserve to be supported.

[2023-01-06; Reflector poll]

Set priority to 3 after reflector poll.

"An alternative resolution would be to wrap the predicate in reference_wrapper."

"I'd prefer blanket wording that says the actual number of copies and/or moves is unspecified when the wording depicts a copy of a function object."

Related to LWG 3049(i).

Proposed resolution:

This wording is relative to N4917.

1. Modify 27.4.4.5 [string.erasure] as indicated:

   >     template<class charT, class traits, class Allocator, class Predicate>
   >       constexpr typename basic_string<charT, traits, Allocator>::size_type
   >         erase_if(basic_string<charT, traits, Allocator>& c, Predicate pred);

   > > -2- Effects: Equivalent to:

   > > >     auto it = remove_if(c.begin(), c.end(), std::move(pred));
   > > >     auto r = distance(it, c.end());
   > > >     c.erase(it, c.end());
   > > >     return r;

2. Modify 23.3.5.5 [deque.erasure] as indicated:

   >     template<class T, class Allocator, class Predicate>
   >       typename deque<T, Allocator>::size_type
   >         erase_if(deque<T, Allocator>& c, Predicate pred);

   > > -2- Effects: Equivalent to:

   > > >     auto it = remove_if(c.begin(), c.end(), std::move(pred));
   > > >     auto r = distance(it, c.end());
   > > >     c.erase(it, c.end());
   > > >     return r;

3. Modify 23.3.7.7 [forward.list.erasure] as indicated:

   >     template<class T, class Allocator, class Predicate>
   >       typename forward_list<T, Allocator>::size_type
   >         erase_if(forward_list<T, Allocator>& c, Predicate pred);

   > > -2- Effects: Equivalent to: `return c.remove_if(std::move(pred));`

4. Modify 23.3.11.6 [list.erasure] as indicated:

   >     template<class T, class Allocator, class Predicate>
   >       typename list<T, Allocator>::size_type
   >         erase_if(list<T, Allocator>& c, Predicate pred);

   > > -2- Effects: Equivalent to: `return c.remove_if(std::move(pred));`

5. Modify 23.3.13.6 [vector.erasure] as indicated:

   >     template<class T, class Allocator, class Predicate>
   >       constexpr typename vector<T, Allocator>::size_type
   >         erase_if(vector<T, Allocator>& c, Predicate pred);

   > > -2- Effects: Equivalent to:

   > > >     auto it = remove_if(c.begin(), c.end(), std::move(pred));
   > > >     auto r = distance(it, c.end());
   > > >     c.erase(it, c.end());
   > > >     return r;

### 3838(i). The last specialization of `incrementable_traits` is under-constrained

Section: 24.3.2.1 [incrementable.traits] Status: New Submitter: Hewill Kang Opened: 2022-12-07 Last modified: 2023-01-06

Priority: 3

View all other issues in [incrementable.traits].

View all issues with New status.

Discussion:

The last specialization of `incremental_traits` requires that the result obtained by subtracting two objects of type `const T` must model `integral`, then apply `make_signed_t` to it as the difference type of type `T`.

However, since `bool` also models `integral`, but is not a valid template argument for `make_signed_t`, we should ban such cases to avoid unnecessary hard errors (online example):

>     #include <ranges>

>     struct Bool {
>       bool operator-(Bool) const;
>     };

>     template<class T>
>     concept can_iota_view = requires(T t) { std::ranges::iota_view(t); };

>     static_assert(!can_iota_view<Bool>); // hard error

[2022-12-13; Minor wording improvements after LWG reflector discussion]

Remove `remove_cv_t` within `nonbool-integral`, because `bool` prvalues cannot be cv-qualified.

[2023-01-06; Reflector poll]

Set priority to 3 after reflector poll.

"I would prefer to place the new checks directly in the requires-clause instead of introducing `nonbool-integral`."

Proposed resolution:

This wording is relative to N4917.

1. Modify 24.3.2.1 [incrementable.traits] as indicated:

   >     namespace std {
   >       template<class T>
   >         concept nonbool-integral = integral<T> && !same_as<T, bool>;         // exposition only

   >       template<class T> struct incrementable_traits { };

   >       […]

   >       template<class T>
   >         requires (!requires { typename T::difference_type; } &&
   >                   requires(const T& a, const T& b) { { a - b } -> nonbool-integralintegral; })
   >       struct incrementable_traits<T> {
   >         using difference_type = make_signed_t<decltype(declval<T>() - declval<T>())>;
   >       };
   >       […]
   >     }

### 3844(i). Non-numeric formats for negative durations

Section: 30.12 [time.format] Status: Open Submitter: Jonathan Wakely Opened: 2022-12-20 Last modified: 2026-06-09

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with Open status.

Discussion:

The wording of 30.12 [time.format] p4 seems to have some unwanted consequences. It says:

> The result of formatting a `std::chrono::duration` instance holding a negative value, or an `hh_mm_ss` object `h` for which `h.is_negative()` is `true`, is equivalent to the output of the corresponding positive value, with a `STATICALLY-WIDEN<charT>("-")` character sequence placed before the replacement of the initial conversion specifier.

Taken literally, I think that means:

>     format("{:%q}", -1s) == "-s"
>     format("{:%t%j}", -25h) == "-\t1"
>     format("{:%p%I}", -11h) == "-am11"

The last one probably doesn't matter (what does -11am mean anyway?) but some of them do matter, for example something like "`{:(%q)%t%Q}"` intends to put the unit suffix first in parens but will print `"(-s)\t1"` which probably isn't what the user wanted.

I think we want to place the sign before the first numeric conversion specifier, not "the initial conversion specifier". That is what Howard's `date::format` and <fmt> both do.

[Issaquah 2023-02-10; LWG issue processing]

Set priority to 3. Proposed a hybrid resolution.

Previous resolution [SUPERSEDED]

> > Two alternatives are presented, choose one of Option A or Option B.

> 1. Modify 30.12 [time.format] as indicated:

>    > Option A

>    > -4- The result of formatting a `std::chrono::duration` instance holding a negative value, or an `chrono::hh_mm_ss` object `h` for which `h.is_negative()` is `true`, is equivalent to the output of the corresponding positive value, with a `STATICALLY-WIDEN<charT>("-")` character sequence placed before the replacement of the initial first conversion specifier that is not one of %n, %p, %q, %t, or %%.

>    > Option B

>    > -4- Among the specifiers, %H, %I, %M, %S, and %T, the The result of formatting a `std::chrono::duration` instance holding a negative value, or an `chrono::hh_mm_ss` object `h` for which `h.is_negative()` is `true`, is equivalent to the output of the corresponding positive value, with a `STATICALLY-WIDEN<charT>("-")` character sequence placed before the replacement of the initial conversion specifier.

Proposed resolution:

This wording is relative to N4928.

1. Modify 30.12 [time.format] as indicated:

   > -4- The result of formatting a `std::chrono::duration` instance holding a negative value, or an `chrono::hh_mm_ss` object `h` for which `h.is_negative()` is `true`, is equivalent to the output of the corresponding positive value, with a `STATICALLY-WIDEN<charT>("-")` character sequence placed before the replacement of the initial first conversion specifier that is one of %H, %I, %M, %S, or %T.

### 3846(i). `iota_view::iterator::operator-` is overconstrained

Section: 25.6.4.3 [range.iota.iterator] Status: New Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2023-02-01

Priority: 3

View other active issues in [range.iota.iterator].

View all other issues in [range.iota.iterator].

View all issues with New status.

Discussion:

Currently, two `iota_view::iterator`s can be subtracted only when the underlying `W` type models `advanceable`, where `advanceable` consists of a series of syntactic and semantic requirements similar to the `random_access_iterator` concept.

However, when `W` is an C++20 iterator type, whether it provides subtraction is irrelevant to its iterator category. In such cases, still requiring `W` to support a series of random access iterator-like operations seems too restrictive. Consider:

        #include <list>
        #include <ranges>

        int main() {
          std::list l{1, 2, 3, 4, 5};
          auto it = std::counted_iterator(l.begin(), l.size());
          auto r = std::views::iota(it, std::next(it, 3));
          auto sz = r.size();           // 3 as expected
          auto d = r.end() - r.begin(); // error: no match for 'operator-'
        }

We can get the correct size of `iota_view` by subtracting two `counted_iterator`s, but we cannot subtract two `iota_view::iterator`s to get their difference, even though the underlying `counted_iterator` already models `sized_sentinel_for` for itself, which is not satisfactory.

I think we should relax the constraints of `iota_view::iterator::operator-` to allow the above case, which also makes it compatible with `iota_view::sentinel::operator-`.

[2023-02-01; Reflector poll]

Set priority to 3 after reflector poll. Several P0 votes, but an objection to P0 on the basis that we don't define what it means to use `sized_sentinel_for` on non-iterators. Others responded that we don't need to, as we only use it with iterators, and do not intend it to be usable with anything else.

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.6.4.3 [range.iota.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<weakly_incrementable W, semiregular Bound>
   > >         requires weakly-equality-comparable-with<W, Bound> && copyable<W>
   > >       struct iota_view<W, Bound>::iterator {
   > >       private:
   > >         W value_ = W();             // exposition only
   > >       public:
   > >         […]
   > >         friend constexpr iterator operator-(iterator i, difference_type n)
   > >           requires advanceable<W>;
   > >         friend constexpr difference_type operator-(const iterator& x, const iterator& y)
   > >           requires advanceable<W> || sized_sentinel_for<W, W>;
   > >       };
   > >     }

   > […]

   >     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
   >       requires advanceable<W> || sized_sentinel_for<W, W>;

   > > -23- Effects: Equivalent to:

   > >       using D = difference_type;
   > >       if constexpr (is-integer-like<W>) {
   > >         if constexpr (is-signed-integer-like<W>)
   > >           return D(D(x.value_) - D(y.value_));
   > >         else
   > >           return (y.value_ > x.value_)
   > >             ? D(-D(y.value_ - x.value_))
   > >             : D(x.value_ - y.value_);
   > >       } else {
   > >         return x.value_ - y.value_;
   > >       }

### 3852(i). `join_with_view::iterator`'s `iter_move` and `iter_swap` should be conditionally `noexcept`

Section: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2024-01-29

Priority: 3

View other active issues in [range.join.with.iterator].

View all other issues in [range.join.with.iterator].

View all issues with New status.

Discussion:

In order to preserve room for optimization, the standard always tries to propagate the `noexcept` specification of custom `iter_move`/`iter_swap` for different iterators.

But for `join_with_view::iterator`, these two specializations are the only ones in the standard that do not have a `noexcept` specification. This is because both invoke `visit` in the function body, and `visit` may throw an exception when the `variant` does not hold a value.

However, implementors are not required to follow the standard practice. Since the `join_with_view::iterator`'s `variant` member only contains two alternative types, both libstdc++ and MSVC-STL avoid heavyweight `visit` calls by simply using multiple if statements. This means that it is still possible to add a conditional `noexcept` specification to these overloads, and there is already a precedent in the standard, namely `common_iterator`. All we need to do is add a Preconditions.

[2023-02-01; Reflector poll]

Set priority to 3 after reflector poll. "The iter_swap specification is wrong since we can swap Pattern and Inner. And this is something implementations can strengthen."

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.7.15.3 [range.join.with.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range V, forward_range Pattern>
   > >       requires view<V> && input_range<range_reference_t<V>>
   > >             && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern>
   > >       template<bool Const>
   > >       class join_with_view<V, Pattern>::iterator {
   > >         […]
   > >         Parent* parent_ = nullptr;                                          // exposition only
   > >         OuterIter outer_it_ = OuterIter();                                  // exposition only
   > >         variant<PatternIter, InnerIter> inner_it_;                          // exposition only
   > >         […]
   > >       public:
   > >         […]
   > >         friend constexpr decltype(auto) iter_move(const iterator& x) noexcept(see below); {
   > >           using rvalue_reference = common_reference_t<
   > >             iter_rvalue_reference_t<InnerIter>,
   > >             iter_rvalue_reference_t<PatternIter>>;
   > >           return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);
   > >         }

   > >         friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   > >           requires indirectly_swappable<InnerIter, PatternIter>;{
   > >           visit(ranges::iter_swap, x.inner_it_, y.inner_it_);
   > >         }
   > >       };
   > >     }

   > […]

   >     friend constexpr decltype(auto) iter_move(const iterator& x) noexcept(see below);

   > > -?- Let `rvalue_reference` be:

   > >       common_reference_t<iter_rvalue_reference_t<InnerIter>, iter_rvalue_reference_t<PatternIter>>

   > > -?- Preconditions: `x.inner_it_.valueless_by_exception()` is `false`.

   > > -?- Effects: Equivalent to: `return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);`

   > > -?- Remarks: The exception specification is equivalent to:

   > >       noexcept(ranges::iter_move(declval<const InnerIter&>())) &&
   > >       noexcept(ranges::iter_move(declval<const PatternIter&>())) &&
   > >       is_nothrow_convertible_v<iter_rvalue_reference_t<InnerIter>, rvalue_reference> &&
   > >       is_nothrow_convertible_v<iter_rvalue_reference_t<PatternIter>, rvalue_reference>

   >     friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   >       requires indirectly_swappable<InnerIter, PatternIter>;

   > > -?- Preconditions: `x.inner_it_.valueless_by_exception()` and `y.inner_it_.valueless_by_exception()` are each `false`.

   > > -?- Effects: Equivalent to: `visit(ranges::iter_swap, x.inner_it_, y.inner_it_)`.

   > > -?- Remarks: The exception specification is equivalent to:

   > >       noexcept(ranges::iter_swap(declval<const InnerIter&>(), declval<const InnerIter&>())) &&
   > >       noexcept(ranges::iter_swap(declval<const PatternIter&>(), declval<const PatternIter&>()))

### 3854(i). §[res.on.exception.handling]/3 should not be applied to all standard library types

Section: 16.4.6.14 [res.on.exception.handling] Status: New Submitter: Jiang An Opened: 2023-01-07 Last modified: 2023-02-01

Priority: 3

View other active issues in [res.on.exception.handling].

View all other issues in [res.on.exception.handling].

View all issues with New status.

Discussion:

Some standard library types, such as `std::pair`, `std::tuple`, and `std::array`, are currently allowed to have a member or element type with a destructor that is `noexcept(false)`. In order to conform to 16.4.6.14 [res.on.exception.handling]/3, these types can't always have implicitly declared destructors because the implicit exception specification may be `noexcept(false)`.

However, it may be intentional to implement these types with implicitly declared destructors, and mainstream implementations (libc++, libstdc++, and MSVC STL) are already doing so.

Furtherly, explicitly declared destructors are not allowed for algorithm result types (26.5 [algorithms.results]), while currently these class templates are allowed to be instantiated with classes with potentially throwing destructors, which is contradictory to 16.4.6.14 [res.on.exception.handling]/3.

[2023-01-29; Daniel comments]

This issue has very much overlap with LWG 3229(i).

[2023-02-01; Reflector poll]

Set priority to 3 after reflector poll. Unclear who the "shall" imposes requirements on. Maybe split into constraint on the library to not put `noexcept(false)` on destructors, and constraint on users that the library can assume destructors don't throw. Should also make it clear which parts of the subclause are normative and which are not.

Proposed resolution:

### 3855(i). `tiny-range` is not quite right

Section: 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Hewill Kang Opened: 2023-01-07 Last modified: 2025-04-28

Priority: 4

View other active issues in [range.lazy.split.view].

View all other issues in [range.lazy.split.view].

View all issues with New status.

Discussion:

Currently, `lazy_split_view` supports input range when the element of the pattern is less than or equal to 1. In order to ensure this condition at compile time, `tiny-range` constrains the type `R` to model `sized_range` and requires `(remove_reference_t<R>::size() <= 1)` to be a constant expression.

However, modeling a `sized_range` does not guarantee that `ranges::size` will be evaluated by `R::size()`. For example, when `disable_sized_range<R>` is specialized to `true` or `R::size()` returns a non-integer-like type, `ranges::size` can still compute the size by subtracting the iterator-sentinel pair when both satisfy `sized_sentinel_for`.

Since the `lazy_split_view`'s iterator uses `R::size()` to get the constant value of the pattern, we must ensure that this is indeed how `ranges::size` is calculated. Also, I think we can simplify `tiny-range` with `bool_constant` in a way similar to LWG 3150(i), which removes the introduction of `require-constant`.

[2023-02-01; Reflector poll]

Set priority to 4 after reflector poll. Only matters for pathological types. Maybe use `requires bool_constant<ranges::size(r) <= 1>`.

Previous resolution [SUPERSEDED]

This wording is relative to N4917.

1. Modify 25.7.16.2 [range.lazy.split.view] as indicated:

   > >     namespace std::ranges {
   > >       template<auto> struct require-constant; // exposition only

   > >       template<class R>
   > >       concept tiny-range = // exposition only
   > >          sized_range<R> &&
   > >          requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   > >          (remove_reference_t<R>::size() <= 1);

   > >       template<input_range V, forward_range Pattern>
   > >         requires view<V> && view<Pattern> &&
   > >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
   > >                  (forward_range<V> || tiny-range<Pattern>)
   > >       class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> {
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     template<class R>
   >     concept tiny-range = // exposition only
   >        sized_range<R> &&
   >        requires { requires bool_constant<(remove_reference_t<R>::size() <= 1)>::value; };

   > > -?- Given an lvalue `r` of type `remove_reference_t<R>`, `R` models `tiny-range` only if `ranges::size(r)` is evaluated by `remove_reference_t<R>::size()`.

   >     constexpr lazy_split_view(V base, Pattern pattern);

   > > -1- Effects: : Initializes `base_` with `std::move(base)`, and `pattern_` with `std::move(pattern)`.

[2025-04-27, Hewill provides alternative wording]

Proposed resolution:

This wording is relative to N5008.

1. Modify 25.7.16.2 [range.lazy.split.view] as indicated:

   > [Drafting note: This benefits from P2280 that a call to a member function of a non-constexpr object can be a constant expression if it does not actually access the member.

   > This would make `views::lazy_split(r, span<int, 0>{})` well-formed, which can be seen as an enhancement.]

   >     namespace std::ranges {
   >       template<auto> struct require-constant;                       // exposition only

   >       template<class R>
   >       concept tiny-range =                                          // exposition only
   >         sized_range<R> &&
   >         requires (R& r) { requires bool_constant<ranges::size(r) <= 1>::value; }
   >         requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   >         (remove_reference_t<R>::size() <= 1);

   >       template<input_range V, forward_range Pattern>
   >         requires view<V> && view<Pattern> &&
   >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
   >                  (forward_range<V> || tiny-range<Pattern>)
   >       class lazy_split_view::view_interface<lazy_split_view<V, Pattern>> {
   >         […]
   >       };
   >       […]
   >     }

2. Modify 25.7.16.5 [range.lazy.split.inner] as indicated:

   > [Drafting note: We can't use `if constexpr (ranges::size(i_.parent_->pattern_) == 0)` here because it is not a constant expression, and it seems more intuitive to just use `ranges::empty` combined with runtime `if` which is always well-formed. Note that the PR does not seek the aggressive optimization that minimizes the instantiation as this is not the intent of the current design (for example, `outer-iterator& operator++()` can be specialized for the case where `Pattern::size() == 0` to save some O(1) comparisons), library implementations are free to optimize as it pleases.]

   >     constexpr inner-iterator& operator++();

   > > -5- Effects: Equivalent to:

   > > >     incremented_ = true;
   > > >     if constexpr (!forward_range<Base>) {
   > > >       if (ranges::empty(i_.parent_->pattern_))
   > > >       if constexpr (Pattern::size() == 0) {
   > > >         return *this;
   > > >       }
   > > >     }
   > > >     ++i_.current;
   > > >     return *this;

### 3856(i). Unclear which conversion specifiers are valid for each chrono type

Section: 30.12 [time.format] Status: New Submitter: Tam S. B. Opened: 2023-01-14 Last modified: 2023-05-30

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with New status.

Discussion:

30.12 [time.format]/3:

> If the formatted object does not contain the information the conversion specifier refers to, an exception of type `format_error` is thrown.

30.12 [time.format]/6:

> If the type being formatted does not contain the information that the format flag needs, an exception of type `format_error` is thrown.

It's unclear how to determine if a type contain the needed information, and implementations diverge.

For example, consider

>     #include <chrono>
>     #include <format>

>     auto f(std::chrono::month_day_last mdl) {
>       return std::format("{:%j}", mdl);
>     }

Both libstdc++ and libc++ produce a compile-time error, claiming that the argument does not contain the information, while MSVC STL throws `format_error` at run time unless `mdl` is `January/last`, in which case the function returns "031".

Another interesting case is `format("{:%d}", mdl)` where the value can be printed for all months except February, which requires a year to know how many days it has.

A related example from Jonathan Wakely:

>     std::chrono::weekday_indexed wdi(Monday, 7);  // 7th Monday in the month
>     assert( ! wdi.ok() );
>     assert( wdi.weekday().ok() );
>     std::format("{:%a}", wdi);

For `%a` the required information is "a valid weekday", and arguably this does contain a valid weekday. On the other hand, there's no 7th Monday, so this isn't valid. Should this throw or not?

This was discussed by LWG and Howard Hinnant summarized the intended behaviour as:

> "The intention of 30.12 [time.format]/6 is to address things like formatting a `duration` with `%F`. A `duration` doesn't contain the calendrical information that `%F` requires (year, month, day). Ditto for using `%a` (weekday name) with a `year`. It is meant to address mismatching types and flags, and not meant to address values."

The type `chrono::weekday` does contain the information needed to print a weekday. A specific invalid value doesn't change that. The type `chrono::month_day_last` does not contain the information needed to print the day of the year. A specific value where the day can be known doesn't change that. The day of month is more interesting, and might need more discussion.

Jonathan proposed adding more examples to clarify the intention that only the type matters, and not the value. There is some redundancy between p3 and p6. Referring to "the formatted object" in p3 seems unclear. Saying "type" as in p6 is better. But p6 refers to "format flag" which is not defined, whereas p3 uses "conversion specifier" (defined at the start of that paragraph). The two uses of "flag" in p6 look like remnants from the earlier `chrono::format` feature that was replaced by integration with `std::format`.

[2023-02-01; Reflector poll]

Set priority to 3 after reflector poll.

[2023-05-30; Jonathan adds wording]

Proposed resolution:

This wording is relative to N4950.

1. Modify 30.12 [time.format] as indicated:

   > -3- Each conversion specifier conversion-spec is replaced by appropriate characters as described in Table 101 ([tab:time.format.spec]); the formats specified in ISO 8601:2004 shall be used where so described. Some of the conversion specifiers depend on the formatting locale. If the string literal encoding is a Unicode encoding form and the locale is among an implementation-defined set of locales, each replacement that depends on the locale is performed as if the replacement character sequence is converted to the string literal encoding. If the formatted objecttype being formatted does not contain the information the conversion specifier refers to, an exception of type `format_error` is thrown.

   > [Example ?: A `duration` does not contain enough information to format as a weekday using `%w`. A `weekday_indexed` does contain enough information to format using `%w` and `Monday[7]` can be formatted as `"1"` even though `Monday[7].ok()` is `false`. A `month_day` does not contain enough information to format as the day of the year using `%j`, even when the `month()` part is `January`. — end example]

   > However, if a flag refers to a "time of day" (e.g., `%H`, `%I`, `%p`, etc.), then a specialization of `duration` is interpreted as the time of day elapsed since midnight.

   > -4- The result of formatting a `std::chrono::duration` instance holding a negative value, or an `hh_mm_ss` object `h` for which `h.is_negative()` is `true`, is equivalent to the output of the corresponding positive value, with a `STATICALLY-WIDEN<charT>("-")` character sequence placed before the replacement of the initial conversion specifier.

   > [Example 1:

   >     cout << format("{:%T}", -10'000s);          // prints: -02:46:40
   >     cout << format("{:%H:%M:%S}", -10'000s);    // prints: -02:46:40
   >     cout << format("minutes {:%M, hours %H, seconds %S}", -10'000s);
   >                                                 // prints: minutes -46, hours 02, seconds 40

   > — end example]

   > -5- Unless explicitly requested, the result of formatting a chrono type does not contain time zone abbreviation and time zone offset information. If the information is available, the conversion specifiers `%Z` and `%z` will format this information (respectively).

   > [Note 1: If the information is not available and a `%Z` or `%z` conversion specifier appears in the chrono-format-spec, an exception of type `format_error` is thrown, as described above. — end note]

   > -6- If the type being formatted does not contain the information that the format flag needs, an exception of type `format_error` is thrown, as described above.

   > [Example 2: A `duration` does not contain enough information to format as a `weekday`. — end example]

   > However, if a flag refers to a "time of day" (e.g., `%H`, `%I`, `%p`, etc.), then a specialization of `duration` is interpreted as the time of day elapsed since midnight.

### 3863(i). Is `input_iterator` guaranteed to have `iter_const_reference_t`?

Section: 24.5.3.2 [const.iterators.alias] Status: New Submitter: Hewill Kang Opened: 2023-01-26 Last modified: 2023-02-06

Priority: 2

View all issues with New status.

Discussion:

In the C++20 iterator system, `input_iterator` is guaranteed to have a common reference, which reflects the `indirectly_readable` requires `common_reference_t<iter_reference_t<I>&&, iter_value_t<I>&>` to be a valid type.

However, for `iter_const_reference_t` which with a similar form:

>     template<indirectly_readable It>
>       using iter_const_reference_t =
>         common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>;

it is still theoretically possible to create an `input_iterator` that does not have a valid `iter_const_reference_t`, for example:

>     #include <iterator>

>     struct ref {
>       ref(int&);
>     };

>     struct rref {
>       rref(const int&);
>       rref(const ref&);
>     };

>     struct I {
>       using value_type = int;
>       using difference_type = std::ptrdiff_t;

>       ref operator*() const;
>       I& operator++();
>       I operator++(int);

>       friend rref iter_move(const I&);
>     };

>     static_assert(std::input_iterator<I>); // pass
>     using CR = std::iter_const_reference_t<I>;  // error: no type named 'type' in 'struct std::common_reference<const int&&, ref>'

which causes `basic_const_iterator<I>` to produce a hard error internally when it is instantiated.

[2023-02-06; Reflector poll]

Set priority to 2 after reflector poll. Seems contrived, should probably constrain not give a hard error. The question of whether each range needs to have const reference to elems is important.

Proposed resolution:

### 3864(i). `zip` over range of reference to an abstract type

Section: 25.7.25 [range.zip] Status: New Submitter: Barry Revzin Opened: 2023-01-28 Last modified: 2023-02-06

Priority: 4

View all issues with New status.

Discussion:

Consider:

>     #include <ranges>

>     struct Abstract {
>       virtual ~Abstract() = default;
>       virtual int f() = 0;
>     };

>     struct Concrete : Abstract {
>       int f() override { return 42; }
>     };

>     int main() {
>       Concrete c[10];

>       auto xformed = c | std::views::transform([](Concrete& c) -> Abstract& {
>         return c;
>       });

>       for (Abstract& a : xformed) { }  // ok

>       auto zipped = std::views::zip(xformed);

>       for (auto&& [a] : zipped) { }    // error
>     }

Here, `xformed` is a range whose reference type is `Abstract&` and whose `value_type` is `Abstract`. Even though you can't actually create a value of that `value_type`, that's okay here, because no code is actually trying to do so.

On the other hand, `zipped` is a range whose reference type is `std::tuple<Abstract&>` and whose `value_type` is `std::tuple<Abstract>`. No code here is actually trying to construct a `value_type` either, but this code fails because simply instantiating `std::tuple<Abstract>` is an error. There's no other possible `value_type` for `zipped` to have, `std::tuple<Abstract>` is correct — it's just that it happens to be an ill-formed type in this context. There are workarounds for this case — you would have to make `xformed` be a range of `Abstract*` or, probably better, a range of `reference_wrapper<Abstract>` instead.

This is unfortunate because many (most?) algorithms don't actually make any use of a range's `value_type`. The ones that do (like `ranges::min`) obviously could not work, but currently we end up rejecting all uses. Probably the only possible way to make this work is to allow `value_type` to be `void` (or absent), but it is currently a fairly fundamental type due to its use in `indirectly_readable` to identify input iterators.

[2023-02-06; Reflector poll]

Set priority to 4 after reflector poll. Several votes for NAD. Maybe `tuple<Abstract>` should be explicitly made ill-formed (currently seems underspecified) or should be "disabled" like invalid `hash` specializations and formatters.

Proposed resolution:

### 3868(i). Constrained algorithms should not require `output_iterator`

Section: 26.7.5 [alg.replace], 26.7.6 [alg.fill] Status: LEWG Submitter: Hewill Kang Opened: 2023-01-29 Last modified: 2024-06-28

Priority: 4

View all other issues in [alg.replace].

View all issues with LEWG status.

Discussion:

In C++20, `output_iterator` is required to support `*o++ = t` mainly for backward compatibility, that is to say, in addition to avoiding needless breakage, this semantic is actually not very useful, as it can be equivalently replaced by `*o = t; ++o;`.

This is reflected in the current implementation for constrained algorithms in libstdc++ and MSVC-STL. Even if the algorithm explicitly requires `output_iterator`, there is no code of the form `*o++ = t` in practice, and the latter of a more generic form is used instead.

It seems to me that constrained algorithms should never require `output_iterator`, since there really isn't any desirable reason to use `*o++ = t` in the new iterator system. It would be more appropriate to relax `output_iterator` to `weakly_incrementable` (or `input_or_output_iterator`) and `indirectly_writable`, given that many constrained algorithms already do that.

[2023-02-06; Reflector poll]

Set priority to 4 after reflector poll. Send to LEWG. Several votes for NAD.

[St. Louis 2024-06-28; LWG and SG9 joint session]

Poll: SG9 and LWG believe this is not a defect, but we do agree that there are inconsistencies that need a paper to address more thoroughly

    |SF| F| N| A|SA|
    | 6| 2| 0| 0| 0|

Proposed resolution:

This wording is relative to N4928.

1. Modify 26.4 [algorithm.syn], header `<algorithm>` synopsis, as indicated:

   >     #include <initializer_list>     // see 17.11.2 [initializer.list.syn]

   >     namespace std {
   >       […]
   >       namespace ranges {
   >         template<class I, class O>
   >           using replace_copy_result = in_out_result<I, O>;

   >         template<input_iterator I, sentinel_for<I> S, class T1, class T2,
   >                  weakly_incrementableoutput_iterator<const T2&> O, class Proj = identity>
   >           requires indirectly_writable<O, const T2&> && indirectly_copyable<I, O> &&
   >                    indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T1*>
   >           constexpr replace_copy_result<I, O>
   >             replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value,
   >                          Proj proj = {});
   >         template<input_range R, class T1, class T2, weakly_incrementableoutput_iterator<const T2&> O,
   >                  class Proj = identity>
   >           requires indirectly_writable<O, const T2&> && indirectly_copyable<iterator_t<R>, O> &&
   >                    indirect_binary_predicate<ranges::equal_to,
   >                                              projected<iterator_t<R>, Proj>, const T1*>
   >           constexpr replace_copy_result<borrowed_iterator_t<R>, O>
   >             replace_copy(R&& r, O result, const T1& old_value, const T2& new_value,
   >                          Proj proj = {});

   >         template<class I, class O>
   >           using replace_copy_if_result = in_out_result<I, O>;

   >         template<input_iterator I, sentinel_for<I> S, class T, weakly_incrementableoutput_iterator<const T&> O,
   >                  class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
   >           requires indirectly_writable<O, const T&> && indirectly_copyable<I, O>
   >           constexpr replace_copy_if_result<I, O>
   >             replace_copy_if(I first, S last, O result, Pred pred, const T& new_value,
   >                             Proj proj = {});
   >         template<input_range R, class T, weakly_incrementableoutput_iterator<const T&> O, class Proj = identity,
   >                  indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
   >           requires indirectly_writable<O, const T&> && indirectly_copyable<iterator_t<R>, O>
   >           constexpr replace_copy_if_result<borrowed_iterator_t<R>, O>
   >             replace_copy_if(R&& r, O result, Pred pred, const T& new_value,
   >                             Proj proj = {});
   >       }
   >       […]
   >       namespace ranges {
   >         template<class T, input_or_output_iteratoroutput_iterator<const T&> O, sentinel_for<O> S>
   >           requires indirectly_writable<O, const T&>
   >           constexpr O fill(O first, S last, const T& value);
   >         template<class T, output_range<const T&> R>
   >           constexpr borrowed_iterator_t<R> fill(R&& r, const T& value);
   >         template<class T, input_or_output_iteratoroutput_iterator<const T&> O>
   >           requires indirectly_writable<O, const T&>
   >           constexpr O fill_n(O first, iter_difference_t<O> n, const T& value);
   >       }
   >       […]
   >     }

2. Modify 26.7.5 [alg.replace] as indicated:

   >     […]
   >     template<input_iterator I, sentinel_for<I> S, class T1, class T2, weakly_incrementableoutput_iterator<const T2&> O,
   >              class Proj = identity>
   >       requires indirectly_writable<O, const T2&> && indirectly_copyable<I, O> &&
   >                indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T1*>
   >     constexpr ranges::replace_copy_result<I, O>
   >       ranges::replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value,
   >                            Proj proj = {});
   >     template<input_range R, class T1, class T2, weakly_incrementableoutput_iterator<const T2&> O,
   >              class Proj = identity>
   >       requires indirectly_writable<O, const T2&> && indirectly_copyable<iterator_t<R>, O> &&
   >                indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T1*>
   >     constexpr ranges::replace_copy_result<borrowed_iterator_t<R>, O>
   >       ranges::replace_copy(R&& r, O result, const T1& old_value, const T2& new_value,
   >                            Proj proj = {});

   >     template<input_iterator I, sentinel_for<I> S, class T, weakly_incrementableoutput_iterator<const T&> O,
   >              class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
   >       requires indirectly_writable<O, const T&> && indirectly_copyable<I, O>
   >     constexpr ranges::replace_copy_if_result<I, O>
   >       ranges::replace_copy_if(I first, S last, O result, Pred pred, const T& new_value,
   >                               Proj proj = {});
   >     template<input_range R, class T, weakly_incrementableoutput_iterator<const T&> O, class Proj = identity,
   >              indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
   >       requires indirectly_writable<O, const T&> && indirectly_copyable<iterator_t<R>, O>
   >     constexpr ranges::replace_copy_if_result<borrowed_iterator_t<R>, O>
   >       ranges::replace_copy_if(R&& r, O result, Pred pred, const T& new_value,
   >                               Proj proj = {});

   > > -6- Let E be

   > > […]

3. Modify 26.7.6 [alg.fill] as indicated:

   >     […]
   >     template<class T, input_or_output_iteratoroutput_iterator<const T&> O, sentinel_for<O> S>
   >       requires indirectly_writable<O, const T&>
   >       constexpr O ranges::fill(O first, S last, const T& value);
   >     template<class T, output_range<const T&> R>
   >       constexpr borrowed_iterator_t<R> ranges::fill(R&& r, const T& value);
   >     template<class T, input_or_output_iteratoroutput_iterator<const T&> O>
   >       requires indirectly_writable<O, const T&>
   >       constexpr O ranges::fill_n(O first, iter_difference_t<O> n, const T& value);

   > > -1- Let N be max(0, `n`) for the `fill_n` algorithms, and `last - first` for the `fill` algorithms.

   > > […]

### 3873(i). `join_with_view`'s `const begin` is underconstrained

Section: 25.7.15.2 [range.join.with.view] Status: New Submitter: Hewill Kang Opened: 2023-02-04 Last modified: 2023-02-10

Priority: 3

View all other issues in [range.join.with.view].

View all issues with New status.

Discussion:

In order to ensure that the pattern range is compatible with the inner range, `join_with_view` requires that the two range types must satisfy `compatible-joinable-ranges`, which requires that the value type, reference type, and rvalue reference type of the two range types share a common type.

However, when the two range types are `const`-qualified, there is no guarantee that their common reference type still exists, in which case a hard error may occur since `join_with_view`'s `const begin` does not check for this (online example):

>     #include <ranges>

>     struct S {
>       S(const int&);
>       S(int&&);
>       S(const int&&) = delete;
>     };

>     int main() {
>       const auto r = std::views::single(std::views::single(0))
>                    | std::views::join_with(std::views::single(S{0}));
>       auto e = std::ranges::iter_move(r.begin()); // hard error
>     }

[Issaquah 2023-02-10; LWG issue processing]

Set priority to 3.

Proposed resolution:

### 3882(i). `tuple` relational operators have confused friendships

Section: 22.4.9 [tuple.rel] Status: New Submitter: Corentin Jabot Opened: 2023-02-08 Last modified: 2023-03-22

Priority: 3

View other active issues in [tuple.rel].

View all other issues in [tuple.rel].

View all issues with New status.

Discussion:

In 22.4.9 [tuple.rel]:

>     template<class... TTypes, tuple-like UTuple>
>       constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);

Is defined as a non-member non-friend function that "is to be found via argument-dependent lookup only."

The intent is that it should be defined as a hidden friend in `tuple`.

The current specification is confusing as to which class should contain that hidden friend, or how to otherwise implement that adl only restriction. (An hostile reading may consider it to be a hidden friend of `UTuple`), and does not follow the guidance of P1601 "Recommendations for Specifying ``Hidden Friends''".

We should consider making these `operator==` and `operator<=>` overloads hidden friends of `tuple`, i.e.

>     std::tuple {
>       template<class... TTypes, tuple-like UTuple>
>       friend constexpr bool operator==(const tuple& t, const UTuple& u);
>       template<class... TTypes, tuple-like UTuple>
>       friend constexpr see below operator<=>(const tuple&, const UTuple&);
>     };

[2023-02-18; Daniel provides wording]

Previous resolution [SUPERSEDED]

This wording is relative to N4928.

1. Modify 22.4.2 [tuple.syn], header `<tuple>` synopsis, as indicated:

   >     namespace std {
   >       […]
   >       // 22.4.9 [tuple.rel], relational operators
   >       template<class... TTypes, class... UTypes>
   >         constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);
   >       template<class... TTypes, tuple-like UTuple>
   >         constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);
   >       template<class... TTypes, class... UTypes>
   >         constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
   >           operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);
   >       template<class... TTypes, tuple-like UTuple>
   >         constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);
   >       […]
   >     }

2. Modify 22.4.4 [tuple.tuple], class template `tuple` synopsis, as indicated:

   >     namespace std {
   >       template<class... Types>
   >       class tuple {
   >       public:
   >         […]

   >         template<tuple-like UTuple>
   >           constexpr tuple& operator=(UTuple&&);
   >         template<tuple-like UTuple>
   >           constexpr const tuple& operator=(UTuple&&) const;

   >         // 22.4.9 [tuple.rel], relational operators
   >         template<tuple-like UTuple>
   >           friend constexpr bool operator==(const tuple&, const UTuple&);
   >         template<tuple-like UTuple>
   >           friend constexpr see below operator<=>(const tuple&, const UTuple&);

   >         // 22.4.4.4 [tuple.swap], tuple swap
   >         constexpr void swap(tuple&) noexcept(see below);
   >         constexpr void swap(const tuple&) const noexcept(see below);
   >       };
   >     }

3. Modify 22.4.9 [tuple.rel] as indicated:

   >     template<class... TTypes, class... UTypes>
   >       constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
   >     template<class... TTypes, tuple-like UTuple>
   >       friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);

   > > -1- For the first overload let `UTuple` be `tuple<UTypes...>`. For the second overload let `TTypes` denote the pack `Types`.

   > > […]

   > > -5- Remarks:

   > > 1. (5.1) — The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates to `false`.

   > > 2. (5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.

   >     template<class... TTypes, class... UTypes>
   >       constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
   >         operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
   >     template<class... TTypes, tuple-like UTuple>
   >       friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...>
   >         operator<=>(const tuple<TTypes...>& t, const UTuple& u);

   > > -6- For the second overload, let `TTypes` denote the pack `Types` and `Elems` denotes the pack of types `tuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>`.

   > > […]

   > > -8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.

[2023-03-05; Daniel comments and provides improved wording]

The revised wording ensures that no ambiguity exists between the overload candidates, furthermore the additional wording about being found via argument-dependent lookup only has been eliminated, because the general conventions of 16.4.6.6 [hidden.friends] apply.

The reusage of the exposition-only concept `different-from` is already existing practice in various places of 22.4 [tuple]. We have also existing wording practice where we have an extra Constraints: element added even though a prototype has already a language constraint as part of its signature.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4928.

1. Modify 22.4.2 [tuple.syn], header `<tuple>` synopsis, as indicated:

   >     namespace std {
   >       […]
   >       // 22.4.9 [tuple.rel], relational operators
   >       template<class... TTypes, class... UTypes>
   >         constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);
   >       template<class... TTypes, tuple-like UTuple>
   >         constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);
   >       template<class... TTypes, class... UTypes>
   >         constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
   >           operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);
   >       template<class... TTypes, tuple-like UTuple>
   >         constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);
   >       […]
   >     }

2. Modify 22.4.4 [tuple.tuple], class template `tuple` synopsis, as indicated:

   >     namespace std {
   >       template<class... Types>
   >       class tuple {
   >       public:
   >         […]

   >         template<tuple-like UTuple>
   >           constexpr tuple& operator=(UTuple&&);
   >         template<tuple-like UTuple>
   >           constexpr const tuple& operator=(UTuple&&) const;

   >         // 22.4.9 [tuple.rel], relational operators
   >         template<tuple-like UTuple>
   >           friend constexpr bool operator==(const tuple&, const UTuple&);
   >         template<tuple-like UTuple>
   >           friend constexpr see below operator<=>(const tuple&, const UTuple&);

   >         // 22.4.4.4 [tuple.swap], tuple swap
   >         constexpr void swap(tuple&) noexcept(see below);
   >         constexpr void swap(const tuple&) const noexcept(see below);
   >       };
   >     }

3. Modify 22.4.9 [tuple.rel] as indicated:

   >     template<class... TTypes, class... UTypes>
   >       constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
   >     template<class... TTypes, tuple-like UTuple>
   >       friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);

   > > -1- For the first overload let `UTuple` be `tuple<UTypes...>`. For the second overload let `TTypes` denote the pack `Types`.

   > > -?- Constraints: For the second overload, `different-from<UTuple, tuple>` (25.5.2 [range.utility.helpers]) is `true`.

   > > -2- Mandates: […]

   > > […]

   > > -5- Remarks:

   > > 1. (5.1) — The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates to `false`.

   > > 2. (5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.

   >     template<class... TTypes, class... UTypes>
   >       constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
   >         operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
   >     template<class... TTypes, tuple-like UTuple>
   >       friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...>
   >         operator<=>(const tuple<TTypes...>& t, const UTuple& u);

   > > -6- For the second overload, let `TTypes` denote the pack `Types` and `Elems` denotes the pack of types `tuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>`.

   > > -?- Constraints: For the second overload, `different-from<UTuple, tuple>` (25.5.2 [range.utility.helpers]) is `true`.

   > > -7- Effects: […]

   > > -8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.

### 3883(i). §[support.c.headers.other] Ambiguity in the requirements for includes

Section: 17.15.7 [support.c.headers.other] Status: New Submitter: Alex Mills Opened: 2023-02-09 Last modified: 2023-03-22

Priority: 4

View other active issues in [support.c.headers.other].

View all other issues in [support.c.headers.other].

View all issues with New status.

Discussion:

In 17.15.7 [support.c.headers.other], it states that the `<name.h>` headers behave "as if each name placed in the standard library namespace by the corresponding header is placed within the global namespace scope". There are exceptions for several different definitions in the headers, but it's left ambiguous as to whether their inclusion in the `<name.h>` headers is required or unspecified.

While it is clear that, for example, `::byte` is prohibited, it isn't clear whether `std::byte` is required to be included at all (this applies to the other listed exceptions as well). Though example 1 makes it appear that their inclusion requirements are meant to be left unspecified, paragraph 1 does not provide clarification. Adding specific wording will ensure this will not be misinterpreted in the future.

To do this, we should append a sentence to [support.c.headers.other] p1:

> Whether the listed exceptions are included in their corresponding `<name.h>` headers under the standard library namespace is unspecified.

[2023-03-22; Reflector poll]

Set priority to 4 after reflector poll. "The new wording is confusingly inconsistent with the existing wording in that paragraph. Would prefer '... Whether the listed exceptions are declared in the namespace std by their corresponding `<name.h>` headers is unspecified.'."

Proposed resolution:

This wording is relative to N4928.

1. Modify 17.15.7 [support.c.headers.other] as indicated:

   > -1- Every C header other than `<complex.h>` (17.15.2 [complex.h.syn]), `<iso646.h>` (17.15.3 [iso646.h.syn]), `<stdalign.h>` (17.15.4 [stdalign.h.syn]), `<stdatomic.h>` (32.5.12 [stdatomic.h.syn]), `<stdbool.h>` (17.15.5 [stdbool.h.syn]), and `<tgmath.h>` (17.15.6 [tgmath.h.syn]), each of which has a name of the form `<name.h>`, behaves as if each name placed in the standard library namespace by the corresponding `<cname>` header is placed within the global namespace scope, except for the functions described in 29.7.6 [sf.cmath], the `std::lerp` function overloads (29.7.4 [c.math.lerp]), the declaration of `std::byte` (17.2.1 [cstddef.syn]), and the functions and function templates described in 17.2.5 [support.types.byteops]. It is unspecified whether these names are first declared or defined within namespace scope (6.4.6 [basic.scope.namespace]) of the namespace `std` and are then injected into the global namespace scope by explicit using-declarations (9.10 [namespace.udecl]). Whether the listed exceptions are included in their corresponding `<name.h>` headers under the standard library namespace is unspecified.

### 3888(i). Most ranges uninitialized memory algorithms are underconstrained

Section: 26.11.8 [specialized.construct] Status: New Submitter: Jiang An Opened: 2023-02-17 Last modified: 2023-03-22

Priority: 3

View all other issues in [specialized.construct].

View all issues with New status.

Discussion:

LWG 3870(i) removed the support for constructing objects via cv-qualified lvalues. However, it had not changed anything in the immediate context, which means some previously permitted usages become hard errors instead of substitution failures.

Note that `ranges::uninitialized_default_construct` and `ranges::uninitialized_default_construct_n` are underconstrained even before LWG 3870(i), because `remove_reference_t<iter_reference_t<I>>` may be a `const` type, while only it's cv-unqualified version (`iter_value_t<I>`) is required to be `default_initializable`.

`construct_at` and `ranges::construct_at` are also made underconstrained because the function body is no longer valid when `T` is cv-qualified, which is not reflected in Constraints:.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4928.

1. Modify 26.11.2 [special.mem.concepts] as indicated:

   >     template<class I>
   >     concept nothrow-input-iterator = // exposition only
   >       input_iterator<I> &&
   >       is_lvalue_reference_v<iter_reference_t<I>> &&
   >       same_as<remove_cvref_t<iter_reference_t<I>>, remove_reference_t<iter_reference_t<I>> &&
   >       same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>>;

2. Modify 26.11.8 [specialized.construct] as indicated:

   >     template<class T, class... Args>
   >       constexpr T* construct_at(T* location, Args&&... args);

   >     namespace ranges {
   >       template<class T, class... Args>
   >         constexpr T* construct_at(T* location, Args&&... args);
   >     }

   > > -1- Constraints: `T` is a cv-unqualified type, and tThe expression `::new (declval<void*>()) T(declval<Args>()...)` is well-formed when treated as an unevaluated operand (7.2.3 [expr.context]).

   > > -2- Effects: Equivalent to:

   > > >     return ::new (voidify(*location)) T(std::forward<Args>(args)...);

### 3889(i). `std::(ranges::)destroy_at` should destroy array elements in the decreasing index order

Section: 26.11.9 [specialized.destroy] Status: New Submitter: Jiang An Opened: 2023-02-17 Last modified: 2023-03-22

Priority: 3

View all issues with New status.

Discussion:

Currently, `std::(ranges::)destroy_at` is specified to destroy array elements in the increasing index order (26.11.9 [specialized.destroy]/1.1), which is inconsistent with the decreasing order specified in the core language (11.4.7 [class.dtor]/13) and the order for arrays created by `std::make_shared` and `std::allocate_shared` (mandated by LWG 3005(i)).

Previous resolution [SUPERSEDED]

This wording is relative to N4928.

1. Modify 26.11.9 [specialized.destroy] as indicated:

   >     template<class T>
   >       constexpr void destroy_at(T* location);

   >     namespace ranges {
   >       template<destructible T>
   >         constexpr void destroy_at(T* location) noexcept;
   >     }

   > > -1- Effects:

   > > 1. (1.1) — If `T` is an array type, equivalent to `destroy(rbeginbegin(*location), rendend(*location))`.

   > > 2. (1.2) — Otherwise, equivalent to `location->~T()`.

[2023-02-26; Daniel comments and provides alternative wording]

The suggested fix indeed corrects an inconsistency, but also implies a silent behaviour change at runtime, since at least MSVC STL and libstdc++ implement the array destruction order as specified (others not tested). The below wording therefore suggests to introduce a specific feature macro for this, so that user code can potentially react on this, regardless of potential vendor API breakage hesitations. The natural feature macro to increase would be that which introduced the specific array destruction behavior of `destroy_at`, which was P0896R4, and which introduced `__cpp_lib_ranges`, on the other hand the specification change affects both the `std::ranges` and the `std` forms of `destroy_at`, so it seems plausible to suggest a new, specific feature macro for both `destroy_at` function templates. This is what the proposed wording does.

LWG should clarify whether an entry to C.2 [diff.cpp20] should be added as well.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4928.

1. Modify 17.3.2 [version.syn], header `<version>` synopsis, as indicated and replace the placeholder `YYYYMML` by the year and month of adoption of this issue:

   >     […]
   >     #define __cpp_lib_coroutine         201902L  // also in <coroutine>
   >     #define __cpp_lib_destroy_at        YYYYMML  // also in <memory>
   >     #define __cpp_lib_destroying_delete 201806L  // also in <new>
   >     […]

2. Modify 26.11.9 [specialized.destroy] as indicated:

   >     template<class T>
   >       constexpr void destroy_at(T* location);

   >     namespace ranges {
   >       template<destructible T>
   >         constexpr void destroy_at(T* location) noexcept;
   >     }

   > > -1- Effects:

   > > 1. (1.1) — If `T` is an array type, equivalent to `destroy(rbeginbegin(*location), rendend(*location))`.

   > > 2. (1.2) — Otherwise, equivalent to `location->~T()`.

### 3890(i). ABI issue for integer-class types

Section: 24.3.4.4 [iterator.concept.winc] Status: New Submitter: Jiang An Opened: 2023-02-19 Last modified: 2023-03-22

Priority: 3

View other active issues in [iterator.concept.winc].

View all other issues in [iterator.concept.winc].

View all issues with New status.

Discussion:

Currently integer-class types are required to be wider than every integer type (24.3.4.4 [iterator.concept.winc]/3). As a result, if compiler provides extended integer types later whose width are not less than that of an integer-class type, then integer-class types with that width must be abandoned, which seems ABI-breaking (see also LWG 3828(i)).

I think we should allow some extended integer types to be wider than some integer-class types.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll. "Proposed resolution is insufficient. A lot of the wording depends on the fact that the range of widths of integer-class types and that of integer types are disjoint. And it also disallows signed integer -> unsigned integer-class conversion and by extension mixed ops, which is seriously breaking (e.g., it disallows `x += 1`)."

Proposed resolution:

This wording is relative to N4928.

1. Modify 24.3.4.4 [iterator.concept.winc] as indicated:

   > -3- The range of representable values of an integer-class type is the continuous set of values over which it is defined. For any integer-class type, its range of representable values is either `-2N-1` to `2N-1 - 1` (inclusive) for some integer `N`, in which case it is a signed-integer-class type, or `0` to `2N - 1` (inclusive) for some integer `N`, in which case it is an unsigned-integer-class type. In both cases, `N` is called the width of the integer-class type. The width of an integer-class type is greater than that of every standard integral type of the same signedness.

   > […]

   > -6- Expressions of integer-class type are explicitly convertible to any integer-like type, and implicitly convertible to any integral type or integer-class type of equal or greater width and the same signedness. Expressions of integral type are both implicitly and explicitly convertible to any integer-class type, and implicitly convertible to any integer-class type that can represent all values of the source type. Conversions between integral and integer-class types and between two integer-class types do not exit via an exception. The result of such a conversion is the unique value of the destination type that is congruent to the source modulo `2N`, where `N` is the width of the destination type.

### 3895(i). Various relation concepts are missing default values of the second template parameters

Section: 18.3 [concepts.syn], 24.2 [iterator.synopsis] Status: New Submitter: blacktea hamburger Opened: 2023-02-25 Last modified: 2023-03-22

Priority: 3

View all issues with New status.

Discussion:

`std::indirect_equivalence_relation` and `std::indirect_strict_weak_order` have default values for the second template parameters:

>     template<class F, class I1, class I2 = I1>
>       concept indirect_equivalence_relation = see below;

>     template<class F, class I1, class I2 = I1>
>       concept indirect_strict_weak_order = see below;

But `std::relation`, `std::equivalence_relation`, `std::strict_weak_order`, and `std::indirect_binary_predicate` are missing such default values:

>     template<class R, class T, class U>
>       concept relation = see below;

>     template<class R, class T, class U>
>       concept equivalence_relation = see below;

>     template<class R, class T, class U>
>       concept strict_weak_order = see below;

>     template<class F, class I1, class I2>
>       concept indirect_binary_predicate = see below;

That makes them inconsistent and it should not be expected.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll. "Borderline design change." "Should not change `indirect_binary_predicate`." "NAD, write a paper." "NAD, default argument would make typos compile. Explicit is good."

Proposed resolution:

This wording is relative to N4928.

1. Modify 18.3 [concepts.syn], header `<concepts>` synopsis, as indicated:

   >     // all freestanding
   >     namespace std {
   >       […]

   >       // 18.7.5 [concept.relation], concept relation
   >       template<class R, class T, class U = T>
   >         concept relation = see below;

   >       // 18.7.6 [concept.equiv], concept equivalence_relation
   >       template<class R, class T, class U = T>
   >         concept equivalence_relation = see below;

   >       // 18.7.7 [concept.strictweakorder], concept strict_weak_order
   >       template<class R, class T, class U = T>
   >         concept strict_weak_order = see below;
   >     }

2. Modify 18.7.5 [concept.relation] as indicated:

   >     template<class R, class T, class U = T>
   >       concept relation =
   >         predicate<R, T, T> && predicate<R, U, U> &&
   >         predicate<R, T, U> && predicate<R, U, T>;

3. Modify 18.7.6 [concept.equiv] as indicated:

   >     template<class R, class T, class U = T>
   >       concept equivalence_relation = relation<R, T, U>;

4. Modify 18.7.7 [concept.strictweakorder] as indicated:

   >     template<class R, class T, class U = T>
   >       concept strict_weak_order = relation<R, T, U>;

5. Modify 24.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     […]
   >     namespace std {
   >       […]
   >       // 24.3.6.3 [indirectcallable.indirectinvocable], indirect callables
   >       […]
   >       template<class F, class I1, class I2 = I1>
   >         concept indirect_binary_predicate = see below; // freestanding

   >       template<class F, class I1, class I2 = I1>
   >         concept indirect_equivalence_relation = see below; // freestanding
   >       […]
   >     }

6. Modify 24.3.6.3 [indirectcallable.indirectinvocable] as indicated:

   > -1- The indirect callable concepts are used to constrain those algorithms that accept callable objects (22.10.4 [func.require]) as arguments.

   > >     […]
   > >     template<class F, class I1, class I2 = I1>
   > >       concept indirect_binary_predicate =
   > >         indirectly_readable<I1> && indirectly_readable<I2> &&
   > >         copy_constructible<F> &&
   > >         predicate<F&, iter_value_t<I1>&, iter_value_t<I2>&> &&
   > >         predicate<F&, iter_value_t<I1>&, iter_reference_t<I2>> &&
   > >         predicate<F&, iter_reference_t<I1>, iter_value_t<I2>&> &&
   > >         predicate<F&, iter_reference_t<I1>, iter_reference_t<I2>> &&
   > >         predicate<F&, iter_common_reference_t<I1>, iter_common_reference_t<I2>>;
   > >     […]

### 3896(i). The definition of `viewable_range` is not quite right

Section: 25.4.6 [range.refinements] Status: New Submitter: Hewill Kang Opened: 2023-02-27 Last modified: 2023-03-22

Priority: 4

View all other issues in [range.refinements].

View all issues with New status.

Discussion:

The requirements of `viewable_range` for `view` type is `view<remove_cvref_t<T>> && constructible_from<remove_cvref_t<T>, T>`, that is, when the decayed type of `T` models `view`, it must be constructible from `T`.

This part of the constraint corresponds to first bullet of `views::all` (25.7.6.1 [range.all.general]), which returns `decay-copy(E)` if the decayed type of `E` models `view`.

However, `decay-copy(E)` constraints `convertible_to<T, decay_t<T>>` which is a stronger requirement than `constructible_from`, which is reflected in its rejection of types with explicit copy constructors.

This inconsistency is such that the following causes the range adapter to produce a hard error when invoked (online example):

>     #include <ranges>

>     struct View : std::ranges::view_base
>     {
>       View();
>       explicit View(const View&); // explicit copy constructor
>       View& operator=(const View&);
>       View(View&&);

>       int* begin();
>       int* end();
>     };

>     int main()
>     {
>       View v;
>       auto r = std::views::take(v, 5); // hard error
>     }

[2023-03-22; Reflector poll]

Set priority to 4 after reflector poll. "About as contrived as it gets." "Add generic front matter telling users the library doesn't support types with explicit copy constructors."

Proposed resolution:

This wording is relative to N4928.

1. Modify 25.4.6 [range.refinements] as indicated:

   >     template<class T>
   >       concept viewable_range =
   >         range<T> &&
   >         ((view<remove_cvref_t<T>> && convertible_to<T, remove_cvref_t<T>>constructible_from<remove_cvref_t<T>, T>) ||
   >          (!view<remove_cvref_t<T>> &&
   >           (is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));

### 3898(i). Possibly unintended preconditions for completion functions of `std::barrier`

Section: 32.9.3.3 [thread.barrier.class] Status: New Submitter: Jiang An Opened: 2023-03-02 Last modified: 2023-03-22

Priority: 3

View all issues with New status.

Discussion:

32.9.3.3 [thread.barrier.class]/5 currently says:

> […] `is_nothrow_invocable_v<CompletionFunction&>` shall be `true`.

This requirement introduces a kind of undefined behavior and permits implementation divergence. Currently MSVC STL enforces the requirement, while libstdc++ and libc++ don't.

If implementation divergence is not intended, I don't think it makes much sense to introduce UB in this way. I guess we should either strengthen the requirement to require well-formedness affection or relax it.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll.

Previous resolution [SUPERSEDED]

This wording is relative to N4928.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: Effectively impose a Mandates: requirement.

1. Modify 32.9.3.3 [thread.barrier.class] as indicated:

   > -5- `CompletionFunction` shall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements. Instantiation of `barrier<CompletionFunction>` is ill-formed if `is_nothrow_invocable_v<CompletionFunction&>` is not `true` `is_nothrow_invocable_v<CompletionFunction&>` shall be `true`.

Option B: Clarify that we impose a no-throw precondition here, whose violation causes UB.

1. Modify 32.9.3.3 [thread.barrier.class] as indicated:

   > -3- The phase completion step that is executed at the end of each phase has the following effects:

   > 1. (3.1) — Invokes the completion function, equivalent to `completion()`. If any invocation to the completion function throws an exception, the behavior is undefined.

   > 2. (3.2) — Unblocks all threads that are blocked on the phase synchronization point.

   > […]

   > -5- `CompletionFunction` shall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements. `is_nothrow_invocable_v<CompletionFunction&>` shall be `true`.

[2023-03-22; Jonathan provides improved wording]

Proposed resolution:

This wording is relative to N4928.

1. Modify 32.9.3.3 [thread.barrier.class] as indicated:

   > -3- The phase completion step that is executed at the end of each phase has the following effects:

   > 1. (3.1) — Invokes the completion function, equivalent to `completion()`; if that invocation exits via an exception, the function `std::terminate` is invoked.

   > 2. (3.2) — Unblocks all threads that are blocked on the phase synchronization point.

   > […]

   > -5- `CompletionFunction` shall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements. `is_nothrow_invocable_v<CompletionFunction&>` shall be `true`. A program that instantiates `barrier<CompletionFunction>` is ill-formed if `is_invocable_v<CompletionFunction&>` is `false`.

### 3902(i). Return type of `std::declval<cv void>` should be (cv-unqualified) `void`

Section: 22.2.6 [declval] Status: New Submitter: Jiang An Opened: 2023-03-07 Last modified: 2023-03-22

Priority: 4

View other active issues in [declval].

View all other issues in [declval].

View all issues with New status.

Discussion:

Currently libc++ and libstdc++ determine the return type of `std::declval` like this:

>     template<class _Tp>
>     _Tp&& __declval_ret(int); // selected when _Tp is a referenceable type

>     template<class _Tp>
>     _Tp __declval_ret(long); // selected when _Tp is cv void

>     template<class _Tp>
>     decltype(__declval_ret<_Tp>(0)) declval() noexcept;

This strategy avoids instantiation of class templates. But it also drops cv-qualifiers of the return type when the type is cv `void`, which is different from the standard requirements. Such difference has no impact in normal use of `std::declval`, but is observable via `decltype(std::declval<const void>)` and its friends.

Given maintainers may think it's reasonable to keep the current implementation (see GCC Bugzilla #109049), it may be worthwhile to legitimate such strategy.

Should we make such construction ill-formed?

[2023-03-22; Reflector poll]

Set priority to 4 after reflector poll. "The testcase isn't even valid with the previous 'conforming' libstdc++ implementation." "`declval` isn't an addressable function, so would prefer if this was ill-formed rather than complicating the definition for this case."

Proposed resolution:

This wording is relative to N4928.

1. Modify 22.2.1 [utility.syn], header `<utility>` synopsis, and 22.2.6 [declval] as indicated:

   >     template<class T>
   >       remove_cv_t<add_rvalue_reference_t<T>> declval() noexcept; // as unevaluated operand

### 3906(i). "Undefined address" is undefined

Section: 32.5.8.5 [atomics.types.pointer], 32.5.7.5 [atomics.ref.pointer] Status: New Submitter: Jiang An Opened: 2023-03-18 Last modified: 2023-05-24

Priority: 3

View all other issues in [atomics.types.pointer].

View all issues with New status.

Discussion:

This was originally editorial issue #6185.

The term "undefined address" is used in 32.5.8.5 [atomics.types.pointer]/8 and 32.5.7.5 [atomics.ref.pointer]/6, however, it seems not properly defined and the intended meaning is unclear. The term originally appeared in the paper N2393 and was reused by P0019R8, but no explanation can be found in these papers.

Perhaps "undefined address" is related to the undefined behavior specified in 7.6.6 [expr.add].

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3907(i). Can iterator types of range adaptors and range factories be SCARY?

Section: 25.6 [range.factories], 25.7 [range.adaptors] Status: New Submitter: Jiang An Opened: 2023-03-18 Last modified: 2023-06-01

Priority: 3

View all issues with New status.

Discussion:

[For the term SCARY see N2911 and N2913.]

In 25.6 [range.factories] and 25.7 [range.adaptors], many iterator types are specified as exposition-only nested classes or nested class templates.

This has some observable differences from specifying them as class templates declared in the namespace scope:

1. value types are generally not associated entities of iterators or sentinels during ADL, which prevents unintended overloads to be found;

2. two different range adaptor/factory types, e.g. `iota_view<I, S1>` and `iota_view<I, S2>`, have different iterator types.

The ADL reduction seems preferable. However, is it intended to required to any two different range adaptor/factory types to have two different iterator types, even when the same iterator type can work as expected?

Sentinel types don't seem able to be SCARY as they are dependent on iterator types.

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll.

"The ADL hiding stuff is a red herring - SCARY-ness doesn't prevent ADL hiding. Shouldn't require them to be different types (and similarly should not require const/non-const iterators to be implemented as class templates with a single bool template parameter)."

Proposed resolution:

### 3910(i). The effects of including `<iostream>` on initialization are not yet precisely specified

Section: 31.4.2 [iostream.objects.overview] Status: New Submitter: Jiang An Opened: 2023-03-27 Last modified: 2023-05-24

Priority: 4

View all other issues in [iostream.objects.overview].

View all issues with New status.

Discussion:

LWG 3878(i) specified the effects of importing standard library modules on initialization. However, the effects of including `<iostream>` are not yet precisely specified. A hostile reading may consider this to allow the `ios_base::Init` variable to be a function-local static variable or an inline variable, which defeats the purpose.

[2023-05-24; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

1. Change 31.4.2 [iostream.objects.overview]/5 as indicated:

   > -5- The results of including `<iostream>` in a translation unit shall be as if `<iostream>` defined an instance of `ios_base::Init` with static storage duration ordered initialization (6.10.3.3 [basic.start.dynamic]). Each C++ library module (16.4.2.4 [std.modules]) in a hosted implementation shall behave as if it contains an interface unit that defines an unexported `ios_base::Init` variable with ordered initialization (6.10.3.3 [basic.start.dynamic]).

### 3911(i). `unique_ptr`'s `operator*` is missing a mandate

Section: 20.3.1.3.5 [unique.ptr.single.observers], 20.3.1.4.4 [unique.ptr.runtime.observers] Status: New Submitter: Brian Bi Opened: 2023-03-27 Last modified: 2023-06-01

Priority: 3

View all other issues in [unique.ptr.single.observers].

View all issues with New status.

Discussion:

The return type of `std::unique_ptr<T>::operator*` is `std::add_lvalue_reference_t<T>`, but there is no mandate stating that `*get()` is convertible to that type. There also does not appear to be a mandate that `*get()` is a valid expression; dereferenceability is not part of the Cpp17NullablePointer requirements. A similar issue appears to exist for `std::unique_ptr<T[]>::operator[]`.

[2023-03-28; Reflector poll]

Set priority to 3 after reflector poll.

"It would be nice to Mandate `!reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*get())>`." "noexcept-specifier isn't quite right, conversion from `*get()` to `T&` can throw."

Previous resolution [SUPERSEDED]

This wording is relative to N4944.

1. Add the following bullet before 20.3.1.3.5 [unique.ptr.single.observers] paragraph 1:

   >     constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));

   > > -?- Mandates: `*get()` is a valid expression that is convertible to `add_lvalue_reference_t<T>`.

   > > -1- Preconditions: `get() != nullptr`.

   > > -2- Returns: `*get()`.

2. Add the following bullet before 20.3.1.4.4 [unique.ptr.runtime.observers] paragraph 1:

   >     constexpr T& operator[](size_t i) const;

   > > -?- Mandates: `get()[i]` is a valid expression that is convertible to `T&`.

   > > -1- Preconditions: `i` < the number of elements in the array to which the stored pointer points.

   > > -2- Returns: `get()[i]`.

[2023-04-03; Jonathan provides new wording as requested by LWG]

Proposed resolution:

This wording is relative to N4944.

1. Add the following bullet before 20.3.1.3.5 [unique.ptr.single.observers] paragraph 1:

   >     constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));

   > > -1- Preconditions: `get() != nullptr`.

   > > -2- Returns Effects: Equivalent to: `return` `*get();`.

2. Add the following bullet before 20.3.1.4.4 [unique.ptr.runtime.observers] paragraph 1:

   >     constexpr T& operator[](size_t i) const;

   > > -1- Preconditions: `get() != nullptr` `i` < the number of elements in the array to which the stored pointer points.

   > > -2- Returns Effects: Equivalent to: `return` `get()[i];`.

### 3916(i). `allocator`, `polymorphic_allocator`, and containers should forbid cv-qualified types

Section: 20.2.10 [default.allocator], 20.5.3.1 [mem.poly.allocator.class.general], 23.2 [container.requirements] Status: New Submitter: Stephan T. Lavavej Opened: 2023-04-02 Last modified: 2023-05-24

Priority: 3

View other active issues in [default.allocator].

View all other issues in [default.allocator].

View all issues with New status.

Discussion:

LWG 2447(i) adjusted what is now N4944 16.4.4.6.1 [allocator.requirements.general]/2.1 so that the Cpp17Allocator requirements are specified for cv-unqualified object types only.

However, nothing in 20.2.10 [default.allocator] restricts what `T` in `allocator<T>` can be, except where 20.2.10.2 [allocator.members]/2 and /6 require it to be complete for `allocate()` and `allocate_at_least()` (and by implication, for `deallocate()`). (Long ago, `allocator` had member functions whose signatures implied that `const`-qualified types were forbidden, but those signatures were deprecated and removed. This explains the phrasing of an MSVC static assertion.)

20.5.3.1 [mem.poly.allocator.class.general] says a bit more about `Tp` in `polymorphic_allocator<Tp>` but doesn't forbid anything. It says that if `Tp` is a cv-unqualified object type, then `polymorphic_allocator<Tp>` meets the Cpp17Allocator requirements and allocator completeness requirements, but that's all.

There's some implementation variation here. libstdc++ and MSVC reject `allocator<const int>`. libc++ (as of LLVM 16) accepts it but this appears to have been an extension that they're trying to get rid of (their maintainers may be able to comment further; see llvm-project/commit/a54d028895c91da356a4aaf30e27a5a5b90dd313). These 3 implementations all reject `allocator<volatile int>`, `polymorphic_allocator<const int>`, and `polymorphic_allocator<volatile int>` with varying messages.

The Standard should provide clarity here, by mandating that only cv-unqualified object types be given to `allocator`, `polymorphic_allocator`, and containers. (`allocator<void>` must also be allowed, of course. I forget if `polymorphic_allocator` is supposed to accept `void`.) This would simply Standardize existing/desired practice. While it may seem arcane, attempting to form `vector<const T>` is a common novice mistake — so common that MSVC had to add a `static_assert` to emit a clear error message.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3917(i). Validity of `allocator<void>` and possibly `polymorphic_allocator<void>` should be clarified

Section: 20.2.10 [default.allocator], 20.5.3 [mem.poly.allocator.class] Status: New Submitter: Daniel Krügler Opened: 2023-04-08 Last modified: 2023-05-24

Priority: 3

View other active issues in [default.allocator].

View all other issues in [default.allocator].

View all issues with New status.

Discussion:

Before P0174 had been approved for the working paper, the validity of using `void` as template argument for `std::allocator` was obvious due to the existing specification of the explicit specialization `allocator<void>`.

This specialization was first moved to Annex D ([depr.default.allocator]) and later completely eradicated from the working paper via adoption of P0619.

The problem is that, since then, we have no explicit wording for `std::allocator` to support `void` as template argument any more. We fall now under the constraints for template "components" specified in 16.4.5.8 [res.on.functions] bullet 2.5.

> -2- In particular, the behavior is undefined in the following cases:

> 1. […]

> 2. (2.5) — If an incomplete type (6.9.1 [basic.types.general]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.

But no such allowance wording exists for `allocator<void>` nor for `polymorphic_allocator<void>`, more to the contrary, 16.4.4.6.1 [allocator.requirements.general] only refers to cv-unqualified object types as value types and `void` is not an object type.

Now we could argue that the wording is clear that instantiations of these are invalid, but there exists at least some indication that the instantiations are intended to be allowed.

As of 25.8.5 [coro.generator.promise] bullet (17.3) the static `operator new` members of `std::generator` mention a fall-back of using `allocator<void>`.

20.2.10.1 [default.allocator.general] says that all specializations of the default allocator meet the allocator completeness requirements (16.4.4.6.2 [allocator.requirements.completeness]), but albeit this specification does not specifically exclude the existence of an incomplete value type, the wording here does also not provide a definite statement, that it is valid (The wording originally was provided when we started adding support for (yet) incomplete value types that at some point later will become complete, but `void` can never be completed), since it is mostly focused on the completeness requirement for the allocator type itself.

The situation is similar (albeit maybe not that strong) for `polymorphic_allocator<void>`, since 20.5.3 [mem.poly.allocator.class] p1 has some unusual wording form that says

> -1- A specialization of class template `pmr::polymorphic_allocator` meets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]) if its template argument is a cv-unqualified object type.

and says then in p2:

> -2- A specialization of class template `pmr::polymorphic_allocator` meets the allocator completeness requirements (16.4.4.6.2 [allocator.requirements.completeness]) if its template argument is a cv-unqualified object type.

Again, this wording is not conclusive, whether `void` is intended to be supported, it is certainly not completely ruled out, but that is not strong enough to counterpart 16.4.5.8 [res.on.functions] (2.5). It is maybe worth pointing out that for a while we were considering to use `void` as default template argument for `pmr::polymorphic_allocator`, see e.g. P0339R0, but that thought was later replaced by deciding for `std::byte` instead, which is a complete object type.

I assume that at least the intention exists that `std::allocator` is intended to support incomplete types, maybe also for `polymorphic_allocator`. If `polymorphic_allocator` is intended to support incomplete types as well, we should also amend 20.5.3.3 [mem.poly.allocator.mem] p1 and p8 with a Mandates: element similarly as we did for `std::allocator` via LWG 3307(i).

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

1. Add the following new paragraph at the end of 20.2.10.1 [default.allocator.general] as indicated:

   > -2- `allocator_traits<allocator<T>>::is_always_equal::value` is `true` for any `T`.

   > -?- The template parameter `T` of `allocator` may be an incomplete type.

2. Add the following new paragraph at the end of 20.5.3.1 [mem.poly.allocator.class.general] (possibly just after the class template synopsis) as indicated:

   > -?- The template parameter `Tp` of `polymorphic_allocator` may be an incomplete type.

3. Modify 20.5.3.3 [mem.poly.allocator.mem] as indicated:

   > [Drafting note: The reference to `sizeof(Tp)` gives indirect evidence that we want to exclude incomplete types here, but we cannot rely on the "equivalent to" magic formula, because that is defined conditionally]

   >     [[nodiscard]] Tp* allocate(size_t n);

   > > -?- Mandates: `Tp` is not an incomplete type (6.9.1 [basic.types.general]).

   > > -1- Effects: If `numeric_limits<size_t>::max() / sizeof(Tp) < n`, throws `bad_array_new_length`. Otherwise equivalent to:

   > > >     return static_cast<Tp*>(memory_rsrc->allocate(n * sizeof(Tp), alignof(Tp)));

   > > [Drafting note: We don't need extra wording for the member templates `allocate_object`, `deallocate_object`, `new_object`, `delete_object`, or `construct`, because their semantics does not depend on template parameter `Tp` and the general wording of 16.4.5.8 [res.on.functions] (2.5) again requires the completeness of `T` here.

   > > For `deallocate` we also omit the completeness requirement (as we did so for `allocator::deallocate`), because this is indirectly implied by the existing precondition. ]

### 3920(i). Bad footnotes claiming external linkage for entities defined as macros

Section: 16.4.5.3.4 [extern.names] Status: New Submitter: Alisdair Meredith Opened: 2023-04-12 Last modified: 2023-05-24

Priority: 3

View all issues with New status.

Discussion:

16.4.5.3.4 [extern.names] p1 and p2 reserve certain names with external linkage to the implementation.

Each paragraph has a footnote, stating that such names include `errno`, `setjmp(jmpbuf)`, and `va_end(va_list)`. These names are all defines as macros, not entities with that name and external linkage.

Are these footnotes intended by a normative extension to the list of reserved names with external linkage? If so, they should be promoted to the main text, as they are no longer a note.

If they intend to serve as examples, it is not clear to me what principle is being shown, as there are many other macros that might be in a similar position, and I do not see how to follow the a principle that is being noted, and presumably follows from the normative text.

I think this is intended to be a normative extension to the set of reserved names with external linkage, is limited to exactly those three named macros, so should be promoted as a non-note into the main text, but want to hear what LWG consensus is before drafting wording.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3921(i). Is `std::chrono::duration<std::int64_t, std::ratio<INT64_MAX - 1, INT64_MAX>>{40}` required to be correctly formatted?

Section: 30.12 [time.format] Status: New Submitter: Jiang An Opened: 2023-04-14 Last modified: 2023-05-24

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with New status.

Discussion:

Currently none of MSVC STL, libc++, and libstdc++ correctly formats `duration<int64_t, ratio<INT64_MAX -1, INT64_MAX>>{40}` (Godbolt link). For MSVC and libstdc++, `hh_mm_ss` is used when formatting `duration` and thus `duration_cast` is also involved. And it's extremely easy for `duration_cast` to cause UB when the source type is `duration<int64_t, ratio<INT64_MAX -1, INT64_MAX>>`.

However, 30.12 [time.format] doesn't say `hh_mm_ss` is equivalently used when formatting duration. And it seems that the current wording require `duration` (whose `rep` type is an arithmetic type) to be correctly formatted without UB, even if it is of a weird type or extremely large (e.g. when it is `duration<int64_t, ratio<INT64_MAX, 1>>{INT64_MAX}`).

So, if it is intended that `hh_mm_ss` is used for formatting `duration`, perhaps we should explicitly so specify in 30.12 [time.format]. And we may need to fix initialization of `hh_mm_ss` to reduce UB whenever suitable.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

"NAD. A compile-time error is required when ratio arithmetic overflows, 21.5.4 [ratio.arithmetic] p2"

Proposed resolution:

### 3922(i). It's unclear whether `numeric_limits` can be specialized by users

Section: 17.3.5.1 [numeric.limits.general] Status: New Submitter: Christopher Di Bella Opened: 2023-04-14 Last modified: 2024-07-31

Priority: 3

View other active issues in [numeric.limits.general].

View all other issues in [numeric.limits.general].

View all issues with New status.

Discussion:

16.4.5.2.1 [namespace.std]/p2 notes that "unless explicitly prohibited", a user may conditionally specialize any class template for program-defined types. 17.3.5.1 [numeric.limits.general]/p1 doesn't explicitly prohibit this, but it does create a bit of a grey area with its wording because it describes `numeric_limits` as a class template describing "the implementation's representation" of "the arithmetic types".

Since this type is about what the implementation provides, and about arithmetic types only, it's not clear whether a user can specialize it for their own library type such as `ns::int256`. `ns::int256` isn't an arithmetic type (despite resembling one), so one can interpret 16.4.5.2.1 [namespace.std]/p2's (b) condition to mean that `numeric_limits<ns::int256>` needs to be explicitly specialized as if it were the primary template.

Daniel:

This issue has overlap with LWG 3923(i).

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Several votes for Tentatively Ready with Option A, but a request to consider LWG 3923(i) first/instead of this one.

Proposed resolution:

This wording is relative to N4944.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: (This assumes that the above described scenario is intended to be supported)

1. Add the following new paragraph at the end of 17.3.5.1 [numeric.limits.general] as indicated:

   > [Drafting Note: The particular wording form "emulating an arithmetic type" has been borrowed from Table 99 — Cpp17Clock requirements [tab:time.clock] and 30.5.1 [time.duration.general] p2, respectively.]

   > -?- The `numeric_limits` template may be specialized for program-defined types emulating arithmetic types.

Option B: (This assumes that the above described scenario is not intended to be supported)

1. Add the following new paragraph at the end of 17.3.5.1 [numeric.limits.general] as indicated:

   > [Drafting Note: The particular wording form has been borrowed from 17.13.4.1 [coroutine.handle.general] p2 and 20.2.9.1 [allocator.traits.general] p1, respectively.]

   > -?- If a program declares an explicit or partial specialization of `numeric_limits`, the program is ill-formed, no diagnostic required.

### 3923(i). The specification of `numeric_limits` doesn't clearly distinguish between implementation requirements and user requirements

Section: 17.3.5.1 [numeric.limits.general] Status: New Submitter: Daniel Krügler Opened: 2023-04-15 Last modified: 2023-05-24

Priority: 3

View other active issues in [numeric.limits.general].

View all other issues in [numeric.limits.general].

View all issues with New status.

Discussion:

The wording of 17.3.5.1 [numeric.limits.general] seemingly has not been gone through a similar thorough rewording review which we performed in the past to clean-up the working draft as we did via the series of "Mandating" papers by Marshall Clow (P1458 - P1465 and even more).

17.3.5.1 [numeric.limits.general] contains several nowadays inappropriate wording forms, which don't distinguish well enough between requirements imposed on implementations (Where we shouldn't use "shall" wording in the ambiguous form of "Specializations shall be provided for each arithmetic type") and requirements imposed on user types, this has also caused confusion as expressed in LWG 3922(i). It is "obvious" that a program is intended to be allowed to provide program-defined specializations, but as LWG 3922(i) points out, it is unclear how such a specialization is able to meet the requirement "(b) the specialization meets the standard library requirements for the original template" specified in 16.4.5.2.1 [namespace.std] p2.

Another problem is the usage of the unclear wording "Non-arithmetic standard types", which should be replaced by a more precise wording form.

An additional problem is that we actually already do require an implementation to provide specializations for the (library-provided) integer-class types (24.3.4.4 [iterator.concept.winc]), so contrary to what p6 says, we already have at least one exception, where the library is required to specialize `numeric_limits` for a non-arithmetic type. We should make that a bit clearer here as well.

This issue is related to LWG 3922(i) and paper P1841.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

> [Drafting Note: This wording would also solve LWG issue 3922(i) under the assumption that option A is intended]

1. Modify 17.3.3 [limits.syn], header `<limits>` synopsis, as indicated:

   >     […]
   >     // 17.3.5.1 [numeric.limits.general], class template numeric_limits
   >     template<class T> class numeric_limits;

   >     // 17.3.5.3 [numeric.special], numeric_limits specializations
   >     template<class T> class numeric_limits<const T>;
   >     template<class T> class numeric_limits<volatile T>;
   >     template<class T> class numeric_limits<const volatile T>;

   >     template<> class numeric_limits<bool>;
   >     […]

2. Modify 17.3.5.1 [numeric.limits.general] as indicated:

   > -1- The `numeric_limits` class template provides a C++ program with information about various properties of the implementation's representation of the arithmetic types.

   > […]

   > > [Drafting Note: It is unclear whether the requirement in the following paragraph 2 is intended to apply to program-defined specializations as well. Consider as an example a user-defined arithmetic-like type that provides arbitrary precision arithmetic which may require dynamic memory for certain object constructions. Is it invalid to specialize `numeric_limits` for such a type or may the program-defined specialization deviate from this requirement for at least some of its members?

   > > If we want to make this restriction relaxed for program-defined specializations, further wording would be needed to give that permission]

   > -2- For all members declared `static constexpr` in the `numeric_limits` template, specializations shall define these values in such a way that they are usable as constant expressions.

   > -3- For the `numeric_limits` primary template, all data members are value-initialized and all member functions return a value-initialized object.

   > [Note 1: This means all members have zero or `false` values unless `numeric_limits` is specialized for a type. — end note]

   > -4- An implementation shall provide sSpecializations shall be provided for each arithmetic type, both floating-point and integer, including `bool`. The member `is_specialized` shall beis `true` for all such specializations of `numeric_limits`.

   > -5- The value of each member of a specialization of `numeric_limits` on a cv-qualified type `cv T` shall be equal to the value of the corresponding member of the specialization on the unqualified type `T`.

   > > [Drafting Note: If we had introduced `numeric_limits` today we would likely have only allowed to provide specializations for cv-unqualified program-defined types, but that ship has sailed long ago.

   > > Interestingly currently there doesn't exist a specification that defines under which situations the static member `is_specialized` should be defined `true` or `false` (This touches LWG 205(i)). The wording below does not attempt to improve that situation, but at least clarifies that its value may be different from that of the primary template.

   > > Note that this proposed wording does — opposed to the approach of LWG 3922(i) — not restrict that specializations can only be provided by program-defined types "emulating an arithmetic type", because that would break existing specializations and is also problematic in the light of the non-existing definition of that term.

   > > The below wording strategy gives permission to specialize `numeric_limits` only for non-array object types. An alternative approach could make it a precondition instead to instantiate the template for non-array object types, for example.]

   > -?- A program may specialize the `numeric_limits` template for a program-defined non-array object type. Such a specialization is permitted to define a value for any static member that differs from what the primary template would have defined, as appropriate for that type.

   > [Note: It still has to meet the general requirements specified in subclause 17.3.5.1 [numeric.limits.general] and subclause 17.3.5.3 [numeric.special] — end note].

   > > [Drafting Note: The following restriction is carefully drafted to ensure that a library has the freedom to provide such specializations for "extended" types (That are not necessarily integer-class types). The restriction is intended to apply only to "official" (strict) C++ standard library types]

   > -6- An implementation shall not provide specializations for nNon-arithmetic standard types of the C++ standard library, such as `complex<T>` (29.4.3 [complex]), unless specified otherwise (e.g. for integer-class types, 24.3.4.4 [iterator.concept.winc]), shall not have specializations.

3. Modify 17.3.5.3 [numeric.special] as indicated:

   > [Drafting Note: I have left the "shall" usage in p1, because this seems to be a requirement for program-defined specializations as well. The second sentence of p1 is one of the funny ones which partially look like introductory wording, but also seems to indicate requirements, albeit specified in an unusual way ("meaningful").

   > The extra wording added after p2 below attempts to improve the wording situation caused by LWG 559(i) and does that by following a similar approach as done in 22.4.7 [tuple.helper]. ]

   > -1- All members shall be provided for all specializations. However, many values are only required to be meaningful under certain conditions (for example, `epsilon()` is only meaningful if `is_integer` is `false`). Any value that is not "meaningful" shall be set to `0` or `false`.

   > -2- [Example 1:

   > […]

   > — end note]

   > >     template<class T> class numeric_limits<const T>;
   > >     template<class T> class numeric_limits<volatile T>;
   > >     template<class T> class numeric_limits<const volatile T>;

   > -?- Let `NL` denote `numeric_limits<T>` of the cv-unqualified type `T`. Then the value of each member of these specializations of `numeric_limits` is equal to the value of the corresponding member of the specialization `NL`.

   > -3- The specialization for `bool` shall beis provided as follows:

   > […]

### 3924(i). Stop token data race avoidance requirements unclear

Section: 32.3.1 [thread.stoptoken.intro] Status: New Submitter: Brian Bi Opened: 2023-04-16 Last modified: 2023-05-24

Priority: 3

View all issues with New status.

Discussion:

The first sentence of 32.3.1 [thread.stoptoken.intro]/5 says:

> Calls to the functions `request_stop`, `stop_requested`, and `stop_possible` do not introduce data races.

This could be read as saying that if you have a program that doesn't contain data races, and you change it by adding a call to any of these three functions, such a change does not "introduce" data races into the program. In other words, it could be read as saying that these three functions don't race with any other member functions on the same `stop_token` or `stop_source` object.

I'm guessing the actual intended meaning is that calls to these three functions do not race with each other, because it would be more expensive if concurrent calls with `stop_token::operator=` were also required to not race. (Also, none of these functions can avoid racing with the destructor.)

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

"What about `get_token`? `operator==`?"

"Other functions like `get_stop_token` can also be called concurrently. I think the text is actually correct."

Proposed resolution:

This wording is relative to N4944.

1. Modify 32.3.1 [thread.stoptoken.intro] as indicated:

   > -5- Calls to the functions `request_stop`, `stop_requested`, and `stop_possible` do not introduce data races with each other. A call to `request_stop` that returns `true` synchronizes with a call to `stop_requested` on an associated `stop_token` or `stop_source` object that returns `true`. Registration of a callback synchronizes with the invocation of that callback.

### 3926(i). Which namespace `std` is the mentioned one?

Section: 16.4.5.2.1 [namespace.std] Status: New Submitter: jim x Opened: 2023-04-19 Last modified: 2023-05-24

Priority: 4

View other active issues in [namespace.std].

View all other issues in [namespace.std].

View all issues with New status.

Discussion:

16.4.5.2.1 [namespace.std] p1 says

> Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace `std` or to a namespace within namespace `std`.

Which `std` namespace does the rule intend to refer to? Does the text refer to any namespace named `std`? Consider this case:

>     namespace A{
>        namespace std{  // UB or not?
>           void show(){}
>           namespace B{
>              void fun(){}  // UB or not?
>          }
>       }
>     }
>     int main(){
>     }

A resemble usage like the above example can be seen in a libcudacxx github code, for example.

Suggested resolution:

We may want to say the namespace `std` only refers to the namespace whose declaration inhabits the global scope.

16.4.5.2.1 [namespace.std] p2, p7 that refers to namespace `std` have a similar issue.

[2023-04-24; Ville provides wording]

[2023-05-24; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

1. Modify 16.4.5.2.1 [namespace.std] as indicated:

   > -?- The restrictions on the use of namespace `std` apply only to a top-level namespace named `std`. Namespaces named `std` nested in program-defined namespaces are not restricted this way or otherwise reserved.

   > -1- Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace `std` or to a namespace within namespace `std`.

### 3928(i). Non-top-level namespace `posix` shouldn't be reserved

Section: 16.4.5.2.2 [namespace.posix] Status: New Submitter: Jiang An Opened: 2023-04-29 Last modified: 2023-05-24

Priority: 3

View all issues with New status.

Discussion:

The namespace `posix` is made reserved by N2667.

It is clear in the paper that only `::posix` is intendedly reserved, but the current normative wording is not clear enough on this (see also LWG 3926(i)).

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

1. Modify 16.4.5.2.2 [namespace.posix] as indicated:

   > -1- The behavior of a C++ program is undefined if it adds declarations or definitions to top-level namespace `posix` or to a namespace within that namespace `posix` unless otherwise specified. The top-level namespace `posix` is reserved for use by ISO/IEC/IEEE 9945 and other POSIX standards.

### 3929(i). Preconditions for type traits should be Mandates

Section: 21.3.5.4 [meta.unary.prop], 21.3.7 [meta.rel] Status: New Submitter: Alisdair Meredith Opened: 2023-05-01 Last modified: 2023-06-12

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

Since we have adopted the Constraints/Mandates/Preconditions form of wording, "preconditions" refer to runtime requirements, not compile-time. As such, the column labeled "preconditions" in Table 47: Type property predicates [tab:meta.unary.prop] would be better labeled as "Mandates".

This is an LWG issue and not editorial, and "Mandates" would require the library to diagnose violations, but after reviewing all traits in this table, I believe that is reasonable.

Table 48: Type property queries [tab:meta.unary.prop.query] shows how Mandates: elements can be integrated into the "Value" column if we preferred that approach, but for the number of entries in the first table seems like an aggressive change for consistency.

Similarly, for Table 49: Type relationship predicates [tab:meta.rel] the "Comments" column serves as a "Mandates" feature without using that term, so I suggest changing that column title too.

The other tables and type traits wording already appear to be adapted to the Mandates wording style.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

See also issue 2939(i). We should not turn the preconditions into Mandates without fixing them first.

[2023-06-12; Varna]

During the review P2874R1 the group decided to not change the now decided for Preconditions: element in D.13 [depr.meta.types] p3 into a Mandates: element but would like to solve this by this issue.

Proposed resolution:

This wording is relative to N4944.

1. Change in 21.3.5.4 [meta.unary.prop], Table 47: Type property predicates [tab:meta.unary.prop] the column title "Preconditions" to "Mandates".

2. Change in 21.3.7 [meta.rel], Table 49: Type relationship predicates [tab:meta.rel] the column title "Comments" to "Mandates".

### 3931(i). Too many paper bump `__cpp_lib_ranges`

Section: 17.3.2 [version.syn] Status: New Submitter: Jiang An Opened: 2023-05-03 Last modified: 2024-07-27

Priority: 3

View other active issues in [version.syn].

View all other issues in [version.syn].

View all issues with New status.

Discussion:

Currently MSVC STL implements P2602R2 and P2609R3 in C++20 mode as if they were defect reports. However, since P2387R3 and P2494R2, which are possibly considered pure functionality extensions in C++23, also bump `__cpp_lib_ranges`, it's impossible to detect the status of P2602R2 and P2609R3 in C++20 mode (see the discussion in MSVC STL repo).

This may be a non-defect as P2602R2 and P2609R3 are not officially DRs. However, if these papers (including upcoming P2538R1) are expected to be implemented in C++20 modes, it seems better to establish another feature-test macro (e.g., `__cpp_lib_ranges_mechanism`) for them.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

"Needs a more descriptive name than `mechanism`."

Previous resolution [SUPERSEDED]

This wording is relative to N4944.

1. Modify 17.3.2 [version.syn] as indicated:

   > [Drafting note: It is proposed to restore `__cpp_lib_ranges` to the value denoting P2494R2.]

   >     […]
   >     #define __cpp_lib_ranges                            202302L202207L
   >     […]
   >     #define __cpp_lib_ranges_join_with                  202202L // also in <ranges>
   >     #define __cpp_lib_ranges_mechanism                  202302L
   >       // also in <algorithm>, <functional>, <iterator>, <memory>, <ranges>
   >     #define __cpp_lib_ranges_repeat                     202207L // also in <ranges>
   >     […]

[2024-07-24; Jiang An comments and provides improved wording]

All utilities changed by P2602R2, P2609R3, and P2997R1 are now freestanding, so these proposed macros should also be freestanding.

Proposed resolution:

This wording is relative to N4986.

1. Modify 17.3.2 [version.syn] as indicated:

   > [Drafting notes:

   > - It is proposed to restore `__cpp_lib_ranges` to the value denoting P2494R2.

   > - `__cpp_lib_ranges_accessors` will cover P2602R2.

   > - `__cpp_lib_ranges_indirect_invocation` will cover P2609R3 and P2997R1.

   > - None of P2602R2, P2609R3, and P2997R1 really affected utilities in `<functional>`, so these two proposed macros are not required to be also in `<functional>`.

   > - Proposed macros are freestanding because utilities changed by these papers are all freestanding now.

   > ]

   >     […]
   >     #define __cpp_lib_ranges                            202406L202207L
   >       // also in <algorithm>, <functional>, <iterator>, <memory>, <ranges>
   >     #define __cpp_lib_ranges_accessors                  202211L
   >       // freestanding, also in <algorithm>, <iterator>, <memory>, <ranges>
   >     […]
   >     #define __cpp_lib_ranges_generate_random            202403L // also in <random>
   >     #define __cpp_lib_ranges_indirect_invocation        202406L
   >       // freestanding, also in <algorithm>, <iterator>, <memory>, <ranges>
   >     #define __cpp_lib_ranges_iota                       202202L // also in <numeric>
   >     […]

### 3932(i). Expression-equivalence is sometimes unimplementable when passing prvalue expressions to comparison CPOs

Section: 17.12.6 [cmp.alg] Status: New Submitter: Jiang An Opened: 2023-05-04 Last modified: 2023-05-24

Priority: 3

View other active issues in [cmp.alg].

View all other issues in [cmp.alg].

View all issues with New status.

Discussion:

Currently, comparison CPOs may call ADL-found `strong_order`, `weak_order`, and `partial_order` functions. It might be impossible to meet the expression-equivalent requirement if one passed argument is a prvalue and an ADL-found function is selected, because temporary materialization must take place before entering the CPO's `operator()`.

Perhaps we should say when `E` or `F` is a prvalue expression of an object type, temporary materialization takes place first, and then an xvalue referring to the temporary object is used instead of the original `E` or `F`.

There is currently implementation divergence in these CPOs: when performing internal comparison, libstdc++ and libc++ perfect-forward arguments, while MSVC STL always treats arguments as lvalues.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3933(i). P1467R9 accidentally changed the signatures of certain constructors of `std::complex`

Section: 29.4.3 [complex] Status: New Submitter: Jiang An Opened: 2023-05-16 Last modified: 2023-06-01

Priority: 4

View other active issues in [complex].

View all other issues in [complex].

View all issues with New status.

Discussion:

In C++20 and earlier revisions, there are constructors taking two floating-point numbers by value in explicit specializations of `std::complex` for standard floating-point types. Since P1467R9 has removed these explicit specializations, the corresponding constructor in the primary template that takes arguments by `const T&` are used instead. As a result, the following program becomes ill-formed after the changes.

>     #include <complex>

>     int main()
>     {
>       volatile double x = 0.0;
>       std::complex<double> z{x, x}; // ill-formed due to P1467R9 because const double& cannot be bound to a volatile double lvalue
>     }

Currently, libstdc++ has implemented complex specializations for extended floating-point types, but the corresponding constructors of these specializations takes two arguments by value, which is consistent with old specializations.

It seems that it's unintended to change the signatures of these constructors. Perhaps we should restore the signatures for required specializations.

Daniel:

Not only constructors are affected, but also all assignment operators taking the `value_type` as parameter and I suggest that LEWG should have a look at this issue.

[2023-05-20; Daniel comments and suggests wording]

The wording below attempts to restore the exact previous behaviour: For all floating-point types the function parameter types are "by value" and for other types are "by const reference". The wording adds for specification purposes a dependency to the concept `std::floating_point`, but that doesn't mean that an implementation couldn't realize the required effects without usage of concepts or the `<type_traits>` header.

Note that we have already precedence for similar cases where we later reintroduced concept requirements to library parts where no concept dependencies had exist before, such as the `iterator_traits` specialization for pointers (to object) or the additional constraints of `set`'s member function `iterator erase(const_iterator position)` or the constraint for `reverse_iterator::operator->()`, just to name a few.

One alternative approach could be to switch to "by-value" signatures only for the affected signatures. This could affect user-defined floating-point-like types such as those with an arbitrary precision, therefore I started with the most conservative approach restoring the original effects that was present in the working draft N4910 and older ones. It might we worth pointing out that the existing "setter" functions `imag` and `real` have always been using "by-value" signatures for all specializations.

There exists also the possible argument to close this issue as NAD based on the argument that all existing non-member operators taking a `value_type` argument had always been defined to use `const T&` as parameter (such as the `operator@(const T& lhs, const complex<T>& rhs)` forms).

My main argument to solve this issue as shown below is based on the ground that the refactoring done by P1467R9 was mainly inspired to simplify the existing wording and to make it more easy to integrate the addition of the extended floating-point types here, as quoted from P1467R9 section 6.6. `<complex>`:

> […] The explicit specializations of `std::complex<T>` are removed. The only differences between the explicit specializations was the explicit-ness of the constructors that take a complex number of a different type.

This issue has some overlap with LWG 3934(i), which suggests a yet missing specification for the assignment operator taking the `value_type` as parameter.

[2023-06-01; Reflector poll]

Set priority to 4 after reflector poll.

Several votes for NAD, as this only affects `volatile` arguments, so this might even be an accidental improvement.

Proposed resolution:

This wording is relative to N4950.

1. Modify 29.4.3 [complex], class template `complex` synopsis, as indicated:

   >     namespace std {
   >       template<class T> class complex {
   >       public:
   >         using value_type = T;

   >         constexpr complex(T re = T(), T im = T()) requires floating_point<T>;
   >         constexpr complex(const T& re = T(), const T& im = T()) requires (!floating_point<T>);
   >         […]
   >         constexpr complex& operator= (T) requires floating_point<T>;
   >         constexpr complex& operator= (const T&) requires (!floating_point<T>);
   >         constexpr complex& operator+=(T) requires floating_point<T>;
   >         constexpr complex& operator+=(const T&) requires (!floating_point<T>);
   >         constexpr complex& operator-=(T) requires floating_point<T>;
   >         constexpr complex& operator-=(const T&) requires (!floating_point<T>);
   >         constexpr complex& operator*=(T) requires floating_point<T>;
   >         constexpr complex& operator*=(const T&) requires (!floating_point<T>);
   >         constexpr complex& operator/=(T) requires floating_point<T>;
   >         constexpr complex& operator/=(const T&) requires (!floating_point<T>);
   >         […]
   >       };
   >     }

2. Modify 29.4.4 [complex.members] as indicated:

   >     constexpr complex(T re = T(), T im = T()) requires floating_point<T>;
   >     constexpr complex(const T& re = T(), const T& im = T()) requires (!floating_point<T>);

   > > -1- Postconditions: `real() == re && imag() == im` is `true`.

3. Modify 29.4.5 [complex.member.ops] as indicated:

   > [Drafting note: We have an pre-existing specification hole that the effects of the non-compound assignment operator taking the `value_type` as parameter are nowhere specified. This is going to be submitted as a separate issue, see LWG 3934(i).]

   >     constexpr complex& operator+=(T rhs) requires floating_point<T>;
   >     constexpr complex& operator+=(const T& rhs) requires (!floating_point<T>);

   > > -1- Effects: Adds the scalar value `rhs` to the real part of the complex value `*this` and stores the result in the real part of `*this`, leaving the imaginary part unchanged.

   > > -2- Returns: `*this`.

   >     constexpr complex& operator-=(T rhs) requires floating_point<T>;
   >     constexpr complex& operator-=(const T& rhs) requires (!floating_point<T>);

   > > -3- Effects: Subtracts the scalar value `rhs` from the real part of the complex value `*this` and stores the result in the real part of `*this`, leaving the imaginary part unchanged.

   > > -4- Returns: `*this`.

   >     constexpr complex& operator*=(T rhs) requires floating_point<T>;
   >     constexpr complex& operator*=(const T& rhs) requires (!floating_point<T>);

   > > -5- Effects: Multiplies the scalar value `rhs` by the complex value `*this` and stores the result in `*this`.

   > > -6- Returns: `*this`.

   >     constexpr complex& operator/=(T rhs) requires floating_point<T>;
   >     constexpr complex& operator/=(const T& rhs) requires (!floating_point<T>);

   > > -7- Effects: Divides the scalar value `rhs` into the complex value `*this` and stores the result in `*this`.

   > > -8- Returns: `*this`.

### 3934(i). `std::complex<T>::operator=(const T&)` has no specification

Section: 29.4.3 [complex] Status: New Submitter: Daniel Krügler Opened: 2023-05-20 Last modified: 2023-06-01

Priority: 3

View other active issues in [complex].

View all other issues in [complex].

View all issues with New status.

Discussion:

The class template `complex` synopsis in 29.4.3 [complex] shows the following member function:

>     constexpr complex& operator= (const T&);

but does not specify its semantics. This affects a code example such as the following one:

>     #include <complex>
>     #include <iostream>

>     int main()
>     {
>       std::complex<double> z(1, 1);
>       z = 2;
>       std::cout << z << std::endl;
>     }

This problem exists since the 1998 version of the standard (at that time this was declared in subclause [lib.complex]), but fortunately the three major implementations all behave consistently by assigning the provided value to the real part and nullifying the imaginary part, causing the output `(2, 0)`, which is consistent with the expected behaviour of usual mathematical convention and that of C's built-in `complex` types. We should specify this.

The lack of this member specification was observed while a proposed wording for LWG 3933(i) was prepared.

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4950.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

> [Drafting note: The wording forms used below intentionally deviate from the rest of the [complex.member.ops] wording forms, because it seems much simpler and clearer to follow the wording forms used that specify the effects of `imag` and `real` functions. I decided to use "part" instead of "component", which is shorter and more often used in the rest of the specification]

Option A: This assumes that LWG 3933(i) is considered as NAD and just adds the missing prototype specification assuming that the parameter style of the current working draft is intended.

1. Add a new prototype specification at the very beginning of 29.4.5 [complex.member.ops] as indicated:

   >     constexpr complex& operator=(const T& rhs);

   > > -?- Effects: Assigns the value `rhs` to the real part and the value `T()` to the imaginary part of the complex value `*this`.

   > > -?- Returns: `*this`.

   >     constexpr complex& operator+=(const T& rhs);

   > > […]

Option b: This assumes that LWG 3933(i) will be resolved as initially presented and just adds the missing prototype specification assuming that the parameter style suggesting two mutually excluded overloads is intended. The wording delta is presented against the proposed wording of LWG 3933(i).

1. Add a new prototype specification at the very beginning of 29.4.5 [complex.member.ops] as indicated:

   >     constexpr complex& operator=(T rhs) requires floating_point<T>;
   >     constexpr complex& operator=(const T& rhs) requires (!floating_point<T>);

   > > -?- Effects: Assigns the value `rhs` to the real part and the value `T()` to the imaginary part of the complex value `*this`.

   > > -?- Returns: `*this`.

   >     constexpr complex& operator+=(T) requires floating_point<T>;
   >     constexpr complex& operator+=(const T&) requires (!floating_point<T>);

   > > […]

### 3937(i). I/O manipulators should be specified in terms of base classes

Section: 31.7.7 [std.manip], 31.7.8 [ext.manip], 31.7.9 [quoted.manip] Status: New Submitter: Jonathan Wakely Opened: 2023-05-25 Last modified: 2023-06-01

Priority: 3

View other active issues in [std.manip].

View all other issues in [std.manip].

View all issues with New status.

Discussion:

All the manipulators in 31.7.7 [std.manip] and 31.7.8 [ext.manip] use language of the form:

> An object of unspecified type such that if `out` is an object of type `basic_ostream<charT, traits>` then the expression `out << resetiosflags(mask)` behaves as if [...]

This needs to work for any object with a public and unambiguous base class of type `basic_ostream<charT, traits>`, not only objects of exactly that type.

In C++98 and C++03, `setfill` did actually get this right ("if `out` is (or is derived from) `basic_ostream<charT,traits>`"), but that was "fixed" for C++11 to be consistent with the other manipulators.

31.7.9 [quoted.manip] is slightly different, talking about "an instance of `basic_ostream`", but that's not right either.

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3939(i). §[format.string.std] `char` is not formatted as a character when `charT` is `wchar_t`

Section: 28.5.2.2 [format.string.std] Status: New Submitter: S. B. Tam Opened: 2023-05-26 Last modified: 2023-06-01

Priority: 3

View other active issues in [format.string.std].

View all other issues in [format.string.std].

View all issues with New status.

Discussion:

(See discussion at microsoft/STL/pull/3723)

28.5.2.2 [format.string.std] p21 says:

> The available integer presentation types for integral types other than `bool` and `charT` are specified in Table 68.

When `charT` is `wchar_t`, the ordinary character type `char` falls into this category, and thus a `char` gets formatted as an integer by default, not as a character.

This arguably doesn't affect the `std::format` family, because they are specified in terms of `make_format_args`, which calls the `basic_format_arg` constructor, which converts `char` to `wchar_t` (28.5.8.1 [format.arg] (6.2)). But it does affect the `std::formatter<char, wchar_t>` specialization, which isn't specified to use `basic_format_arg`.

This is especially problematic after P2286R8, which makes `std::formatter<char, wchar_t>` debug-enabled, but there's no debug format for integral types other than `charT`.

Perhaps [format.string.std] should say that the formatting arguments are converted as if through the `basic_format_arg` constructor.

At the time of writing, on libstdc++ and libc++, `std::formatter<char, wchar_t>` formats the argument as a character when no specifier is given (godbolt.org/z/nnsEcvna3), while MSVC STL's `std::formatter<char, wchar_t>` outputs the integer value. But I'm about to change MSVC STL to match the other implementations.

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3941(i). §[atomics.order] inadvertently prohibits widespread implementation techniques

Section: 32.5.4 [atomics.order] Status: Open Submitter: Hans Boehm Opened: 2023-05-27 Last modified: 2025-02-10

Priority: 3

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with Open status.

Discussion:

[Discovered by Ori Lahav, lead author of Repairing Sequential Consistency in C/C++11, which P0668 more or less tried to implement. This description also includes observations from Olivier Giroux and David Goldblatt.]

Currently, we require that the sequential consistency order S is consistent with the extended coherence order, which includes "reads before" and "reads from". This may require sc reads to be consistently ordered even if they see non-sc writes. This is not enforced by some "fence-after-store"/"trailing fence" sc implementations, notably on X86. It may also be violated by compiler transformations that eliminate a sequentially consistent load after a store into the same location, e.g. on sequentially consistent hardware.

As Ori pointed out, this is exhibited by their (SB + rfis) example on x86 reproduced below. I would expect this also happens on other platforms. The (SB + rfis) example:

>     Thread 1: x.store(1, mo_release); r1 = x.load() /* sees 1 */; r2 = y.load() /* sees 0 */
>     Thread 2: y.store(1, mo_release); r1 = y.load() /* sees 1 */; r2 = x.load() /* sees 0 */

This is unintentionally and unexpectedly disallowed by the current standard. We can see this as follows:

We have the following coherence-ordered-before relationships:

1. By 3.1 in 32.5.4 [atomics.order] (reads-from): The store in each thread is coherence-ordered-before the immediately following load.

2. By 3.3 (reads-before): The final load in each thread is coherence-ordered before the store in the other thread.

3. By 3.4 (transitivity): The final load in each thread is coherence-ordered-before the initial load in the other thread.

On the other hand, the initial load in each thread is sequenced-before and thus strongly-happens-before 6.10.2.2 [intro.races] p12.1 the final load in that thread. Thus coherence-ordered-before union strongly-happens-before is cyclic. But the S relation 32.5.4 [atomics.order] p4 must order all the loads, consistent with both strongly-happens-before (p4) and coherence-ordered-before (p4.1), which is clearly impossible.

With the standard x86 implementation, no fences are inserted. Thread 1 and Thread 2 see the stores to `x` and `y` in inconsistent orders, which is allowed by TSO, since each thread can see its own write early. Thus the implementation allows the outcome in question, as expected.

The problem here is an error during the attempt to simplify and translate plv.mpi-sws.org/scfix/paper.pdf to standardese. There is no known issue with the underlying paper.

I have not yet wrapped my head around this sufficiently to be able to suggest a solution, or to understand how difficult that will be. This happened as a consequence of attempting to strengthen SC fences, and not fixing the Power compilation problem (P0668). So we could probably improve matters by reverting the strengthening of sc fences. Clearly, we'd prefer to avoid that. We could just adopt the formulation in the paper, though that seems even harder to motivate informally. Fundamentally, it feels like we inadvertently strengthened (and broke) the semantics of ordinary sc operations, when we intended to only affect fences. The original paper makes more of a distinction there. The question is how we can reformulate that suitably here.

[ Meta-observation from a small off-line discussion that may be relevant to how we phrase the resolution here: The fact that nobody noticed this for a very long time, and implementers were not bothered by it, suggests that the audience for this part of the standard is nearly empty. We conjecture that implementers actually rely on atomics mappings generated by memory model experts, who are more interested in formal models than standardese. A more formal description is likely to increase the size of the audience, and would definitely ease verification and reduce the probability of mistakes like this.]

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll. Send to SG1.

[2025-02-10; SG1 reviewed the issue in March 2024 ]

"we want to address LWG3941 in a way that preserves existing implementations like X86" - unanimous consent.

Proposed resolution:

### 3942(i). Inconsistent use of `const char_type&` in standard specializations of `std::char_traits`

Section: 27.2.4 [char.traits.specializations] Status: New Submitter: Jiang An Opened: 2023-05-27 Last modified: 2023-06-01

Priority: 3

View all other issues in [char.traits.specializations].

View all issues with New status.

Discussion:

In the standard specializations of `std::char_traits` (`std::char_traits<char>` etc.), there are a few member functions take a single character via a `const char_type&` parameter, while other functions take a single character by value.

In C++98, there were more functions taking `const char_type&`. N2349 changed this in C++11 by making some, but not all of them take `char_type`. It is unclear whether the inconsistency is intended, and it seems better for these standard specializations to take a character by value.

However, libstdc++ hasn't implemented the signature changes, perhaps due to ABI concerns. So it might be better to loose the restrictions for the purpose of standardization.

[2023-06-01; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4950.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: This assumes that "by-value" arguments are intended.

1. Modify the class template `char_traits` specialization synopses in 27.2.4.2 [char.traits.specializations.char], 27.2.4.3 [char.traits.specializations.char8.t], 27.2.4.4 [char.traits.specializations.char16.t], 27.2.4.5 [char.traits.specializations.char32.t], and 27.2.4.6 [char.traits.specializations.wchar.t] as indicated:

   >     […]
   >         static constexpr void assign(char_type& c1, const char_type& c2) noexcept;
   >     […]
   >         static constexpr const char_type* find(const char_type* s, size_t n,
   >                                                const char_type& a);
   >     […]

Option B: This assumes that implementation-freedom to keep ABI stability is intended.

> [Drafting Note: It is intended to keep the `assign(s, n, a)` taking the character by value, because the argument may be a character in `[s, s + n)`.]

1. Add a paragraph at the end of 27.2.4.1 [char.traits.specializations.general] as indicated:

   > -?- For each occurrence of the placeholder `const-char-t` in the synopsis of each of these specializations, it is unspecified whether it denotes `char_type` or `const char_type&`. Likewise, for each occurrence of the placeholder `const-int-t` in the synopsis of each of these specializations, it is unspecified whether it denotes `int_type` or `const int_type&`

2. Modify the class template `char_traits` specialization synopses in 27.2.4.2 [char.traits.specializations.char], 27.2.4.3 [char.traits.specializations.char8.t], 27.2.4.4 [char.traits.specializations.char16.t], 27.2.4.5 [char.traits.specializations.char32.t], and 27.2.4.6 [char.traits.specializations.wchar.t] as indicated:

   >     […]
   >         static constexpr void assign(char_type& c1, const char_type&const-char-t c2) noexcept;
   >         static constexpr bool eq(char_typeconst-char-t c1, char_typeconst-char-t c2) noexcept;
   >         static constexpr bool lt(char_typeconst-char-t c1, char_typeconst-char-t c2) noexcept;
   >     […]
   >         static constexpr const char_type* find(const char_type* s, size_t n,
   >                                                const char_type&const-char-t a);
   >     […]
   >         static constexpr int_type not_eof(int_typeconst-int-t c) noexcept;
   >         static constexpr char_type to_char_type(int_typeconst-int-t c) noexcept;
   >         static constexpr int_type to_int_type(char_typeconst-char-t c) noexcept;
   >         static constexpr bool eq_int_type(int_typeconst-int-t c1, int_typeconst-int-t c2) noexcept;
   >     […]

### 3943(i). Clarify lifetime requirements of BasicFormatter and Formatter

Section: 28.5.6.3 [format.formattable] Status: New Submitter: Mark de Wever Opened: 2023-06-01 Last modified: 2023-06-08

Priority: 3

View other active issues in [format.formattable].

View all other issues in [format.formattable].

View all issues with New status.

Discussion:

A bug has been filed against libc++'s format implementation. The question arose whether the parsed chrono-specs should still be available during executing the formatter's `format` member function. Libc++'s implementation requires this MSVC STL's implementation does not.

It turns out there is a lifetime a requirement in the Standard, but it would be good to clarify the intention of the wording. Prosed a small wording improvement and a note to clarify the intention.

Currently the `format` function may use elements in the range `[pc.begin(), pc.end())` that have not been parsed. This does not seem to be in the spirit of the formatting library. The wording change proposes to restrict this range to the parsed range.

[2023-06-08; Reflector poll]

Set priority to 3 after reflector poll.

The proposed resolution is missing a guarantee that the parsed portion of the range is unchanged between the calls to `parse` and `format`. It must remain valid and unchanged.

Proposed resolution:

This wording is relative to N4950.

1. Modify BasicFormatter requirements [tab:formatter.basic] as indicated:

   > Table 73: BasicFormatter requirements [tab:formatter.basic]
   > Expression | Return type | Requirement
   > --- | --- | ---
   > `…`
   > `f.format(u, fc)` | `FC::iterator` | Formats `u` according to the specifiers stored in `*this`, writes the output to `fc.out()`, and returns an iterator past the end of the output range. The output shall only depend on `u`, `fc.locale()`, `fc.arg(n)` for any value `n` of type `size_t`, and the elements in the parsed range of `[pc.begin(), pc.end())` from the last call to `f.parse(pc)`. [Note ?: Using elements in the parsed range of `[pc.begin(), pc.end())` allows the formatter to store references, pointers, or iterators to elements in the parsed range. For example, `formatter<chrono::day>` might store the parsed chrono-specs (30.12 [time.format]) in a `basic_string_view`. — end note]

2. Modify Formatter requirements [tab:formatter] as indicated:

   > Table 74: Formatter requirements [tab:formatter]
   > Expression | Return type | Requirement
   > --- | --- | ---
   > `f.format(t, fc)` | `FC::iterator` | Formats `t` according to the specifiers stored in `*this`, writes the output to `fc.out()`, and returns an iterator past the end of the output range. The output shall only depend on `t`, `fc.locale()`, `fc.arg(n)` for any value `n` of type `size_t`, and the elements in the parsed range of `[pc.begin(), pc.end())` from the last call to `f.parse(pc)`. [Note ?: Using elements in the parsed range of `[pc.begin(), pc.end())` allows the formatter to store references, pointers, or iterators to elements in the parsed range. For example, `formatter<chrono::day>` might store the parsed chrono-specs (30.12 [time.format]) in a `basic_string_view`. — end note]
   > `…`

### 3945(i). §[cstdarg.syn] 'Compatible types' are undefined

Section: 17.14.2 [cstdarg.syn] Status: New Submitter: Lukas Barth Opened: 2023-06-08 Last modified: 2023-06-12

Priority: 3

View other active issues in [cstdarg.syn].

View all other issues in [cstdarg.syn].

View all issues with New status.

Discussion:

Subclause 17.14.2 [cstdarg.syn] p1 bullet (1.2) states (among other things):

> […] If the parameter `parmN` is of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.

Unlike the C standard, the C++ standard has no notion of 'compatible types', thus the meaning of this sentence unclear.

[2023-06-12; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3954(i). Feature-test macros in C headers (`<stddef.h>` etc.)

Section: 17.15.1 [support.c.headers.general] Status: New Submitter: Jiang An Opened: 2023-07-06 Last modified: 2024-02-22

Priority: 3

View all issues with New status.

Discussion:

Several feature-test macros are available in C++ versions of C headers (e.g. `__cpp_lib_byte` is available in `<cstddef>`). However, the current standard wording doesn't seem to make the distinction between C++ library feature-test macros and macros from C, so it's not very clear that whether `<stddef.h>` and its friends are required to provide `__cpp_lib_*` macros in C++.

Presumably, `name.h` should provide macros shown in the synopsis of its corresponding `cname` (as required in C), but should not be required to provide C++ library feature-test macros because such requirement would generally require implementations of the C standard library to change. I think we should make clarification in 17.15.1 [support.c.headers.general].

[2024-02-22; Reflector poll]

Set priority to 3 after reflector poll in July 2023.

[Jonathan commented]

The issue says that "such requirement would generally require implementations of the C standard library to change" but this is not true. A conforming C++ library already needs a C++-aware `<stdlib.h>` and `<math.h>`, and has done so since C++98! We should be cautious about expanding the set of C headers that need to be C++-aware, but if we require `<stdlib.h>` to define these functions:

    constexpr long abs(long);
    constexpr long long abs(long long);

then it can also define `__cpp_lib_constexpr_math`.

We have these `<cxxx>` headers providing feature test macros:

    <cmath>:
    __cpp_lib_constexpr_cmath
     __cpp_lib_hypot
    __cpp_lib_interpolate
    __cpp_lib_math_special_functions

    <cstddef>:
    __cpp_lib_byte

    <cstdlib>:
    __cpp_lib_constexpr_cmath

For `<stdlib.h>` and `<math.h>` the implementation already needs a C++-specific version of the header, because `::abs` is required to be overloaded (and constexpr) in `<stdlib.h>` and `<math.h>` (and all the math functions have to be constexpr even if you include `<math.h>`). So I see no issue here: the `<xxx.h>` headers should obviously define the same macros as the `<cxxx>` headers.

We do not require `::byte` to be in `<stddef.h>`, so maybe we should not require the macro there either. Except that `std::byte` is permitted to be in `<stddef.h>`, just not in the global namespace (see LWG 3883(i)). So maybe SD-6 should simply clarify that the macro indicates the presence of `std::byte`, not `::byte`, and whether `std::byte` and its macro are defined by `<stddef.h>` is unspecified. Also related to LWG 3484(i).

[Jens commented]

It seems 17.15 [support.c.headers] is silent on which macros from the `<cxxx>` headers are made available via the `<xxx.h>` headers, given that 17.15.7 [support.c.headers.other] talks about names placed in the standard library namespace (macros don't fit that description).

[Ben noted some additional macros]

    #define __cpp_lib_freestanding_cstdlib      new-val // freestanding, also in <cstdlib>, <cmath>
    #define __cpp_lib_freestanding_cstring      new-val // freestanding, also in <cstring>
    #define __cpp_lib_freestanding_cwchar       new-val // freestanding, also in <cwchar>
    #define __cpp_lib_freestanding_errc         new-val // freestanding, also in <cerrno>, <system_error>

Proposed resolution:

### 3955(i). Add `noexcept` to several `repeat_view[::iterator]` member functions

Section: 25.6.5.2 [range.repeat.view], 25.6.5.3 [range.repeat.iterator] Status: New Submitter: Hewill Kang Opened: 2023-07-06 Last modified: 2023-10-30

Priority: 3

View all other issues in [range.repeat.view].

View all issues with New status.

Discussion:

Several member functions of `repeat_view::iterator` only operate on its integer member, indicating that they do not throw.

Similarly, `repeat_view::size` should also be `noexcept` since it just performs the integer conversion.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. Some votes for NAD. "The iterator changes violate the Lakos rule."

Proposed resolution:

This wording is relative to N4950.

1. Modify 25.6.5.2 [range.repeat.view], class template `repeat_view` synopsis, as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<move_constructible T, semiregular Bound = unreachable_sentinel_t>
   >         requires (is_object_v<T> && same_as<T, remove_cv_t<T>> &&
   >                   (integer-like-with-usable-difference-type<Bound> ||
   >                    same_as<Bound, unreachable_sentinel_t>))
   >       class repeat_view : public view_interface<repeat_view<T, Bound>> {
   >       private:
   >         […]
   >       public:
   >         […]

   >         constexpr auto size() const noexcept requires (!same_as<Bound, unreachable_sentinel_t>);
   >       };
   >       […]
   >     }

   > […]

   >     constexpr auto size() const noexcept requires (!same_as<Bound, unreachable_sentinel_t>);

   > > -9- Effects: Equivalent to: `return to-unsigned-like(bound_);`

2. Modify 25.6.5.3 [range.repeat.iterator], class `repeat_view::iterator` synopsis, as indicated:

   >     namespace std::ranges {
   >       template<move_constructible T, semiregular Bound>
   >         requires (is_object_v<T> && same_as<T, remove_cv_t<T>> &&
   >                   (integer-like-with-usable-difference-type<Bound> ||
   >                    same_as<Bound, unreachable_sentinel_t>))
   >       class repeat_view<T, Bound>::iterator {
   >       private:
   >        using index-type =                          // exposition only
   >           conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>;
   >        const T* value_ = nullptr;                  // exposition only
   >        index-type current_ = index-type();         // exposition only

   >        constexpr explicit iterator(const T* value, index-type b = index-type());   // exposition only

   >       public:
   >         […]
   >         constexpr iterator& operator++() noexcept;
   >         constexpr iterator operator++(int) noexcept;

   >         constexpr const T& operator[](difference_type n) const noexcept;

   >         friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept;
   >         friend constexpr auto operator<=>(const iterator& x, const iterator& y) noexcept;

   >         friend constexpr difference_type operator-(const iterator& x, const iterator& y) noexcept;
   >       };
   >     }

   > […]

   >     constexpr iterator& operator++() noexcept;

   > > -5- Effects: Equivalent to:

   > > >     ++current_;
   > > >     return *this;

   >     constexpr iterator operator++(int) noexcept;

   > > -6- Effects: Equivalent to:

   > > >     auto tmp = *this;
   > > >     ++*this;
   > > >     return tmp;

   > […]

   >     friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept;

   > > -15- Effects: Equivalent to: `return x.current_ == y.current_;`

   >     friend constexpr auto operator<=>(const iterator&x, const iterator& y) noexcept;

   > > -16- Effects: Equivalent to: `return x.current_ <=> y.current_;`

   > […]

   >     friend constexpr difference_type operator-(const iterator& x, const iterator& y) noexcept;

   > > -19- Effects: Equivalent to:

   > > >     return static_cast<difference_type>(x.current_) - static_cast<difference_type>(y.current_);

### 3959(i). Should the comparator of `std::flat_map`/`std::flat_multimap` be copied twice in some operations?

Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap] Status: New Submitter: Jiang An Opened: 2023-07-23 Last modified: 2025-10-21

Priority: 3

View other active issues in [flat.map].

View all other issues in [flat.map].

View all issues with New status.

Discussion:

Currently, some operations of `std::flat_map` are specified to construct a `key_equiv` object for calling `ranges::unique` to remove duplicated elements. It seems that the comparator is required to be copied twice before calling `ranges::unique`.

If we make `key_equiv` to an aggregate class, then the comparator will be only copied once. Furtherly, if we use a `std::reference_wrapper<const key_compare>` as the member of `key_equiv`, then we will be able to avoid copying. Such differences seem to be observable.

Should we require to avoid copying, or explicitly say that implementations can either make copies or avoid copying?

Likewise, `std::flat_map::value_comp` and `std::flat_multimap::value_comp` seem to be required to copy the comparator twice due to the exposition-only constructor of `value_compare`.

Should we make the constructor take a `const` reference?

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

We should provide blanket wording to specify that comparators, hashers, allocators, etc. can be copied as often or as little as needed. Consider generalizing 3049(i).

Proposed resolution:

### 3960(i). How does `chrono::parse` handle duplicated data?

Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2023-11-03

Priority: 3

View other active issues in [time.parse].

View all other issues in [time.parse].

View all issues with New status.

Discussion:

A call to `chrono::parse` can encounter redundant or contradictory data, e.g.

    stringstream("2023 2022") >> chrono::parse("%Y %C%y", yr);

or even simpler:

    stringstream("2023 2022") >> chrono::parse("%Y %Y", yr);

These cases can both be successfully parsed, as the input stream contains the expected values in the expected format. But what value will `yr` have after it returns?

[2023-11-02; Reflector poll]

Set priority to 3 after reflector poll. "Should allow setting `failbit` if an implementation does detect inconsistent values." "Yes, that's what MSVC does, so the wording needs something like: it is unspecified whether `setstate(ios_base::failbit)` is called on the `basic_istream` or whether an unspecified value is assigned to the `p` argument."

Proposed resolution:

This wording is relative to N4950.

1. Modify 30.13 [time.parse] as indicated:

   > -17- If the `from_stream` overload fails to parse everything specified by the format string, or if insufficient information is parsed to specify a complete duration, time point, or calendrical data structure, `setstate(ios_base::failbit)` is called on the `basic_istream`.

   > -?- A format string can contain multiple flags that correspond to the same information, (e.g., `"%Y %C%y"` or `"%R %H:%M"`). If such a format string is successfully parsed but the extracted dates or times are not consistent, the value assigned to the `tp` argument is unspecified.

   > [Example:

   >     chrono::year y;
   >     stringstream("1905 1915") >> parse("%Y %C%y", y);

   > The value of `y` is unspecified; `y.ok()` can be `false`. — end example]

### 3961(i). Does `chrono::parse` check format strings?

Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2023-11-03

Priority: 3

View other active issues in [time.parse].

View all other issues in [time.parse].

View all issues with New status.

Discussion:

30.13 [time.parse] p16 says: "If the type being parsed cannot represent the information that the format flag refers to, `is.setstate(ios_base::failbit)` is called." Note it says "the format flag" singular. I had interpreted this as meaning that if the entire format string doesn't provide the info needed by the type, it fails. But that's not what it says. It says that parsing fails if the type cannot represent "the information that the format flag refers to". Which format flag? Presumably this rule applies to each of them in turn. So `"Monday 13:00" >> parse("%a %R", sec)` is required to fail, rather than ignore the %a part and set `sec` to `13h`. I don't think that is the intended design. I think the phrasing of this rule is backwards. It shouldn't be focused on what info is provided by the format flag, but what info is needed by the type.

What should happen when `chrono::parse` is given a meaningless format string like "%", or "%E", or "%Ea" or "%99a"? Presumably that should set failbit, but I don't think we actually say so. If the implementation should set failbit, is it conforming to do so before extracting any characters?

Is it conforming to set failbit before extracting any characters if the format string can never succeed for the parsable type? Consider:

    std::chrono::seconds sec;
    std::cin >> parse("%a", sec);

The type being parsed (`seconds`) cannot represent the information that %a (a weekday) refers to, so we should set `cin.setstate(ios_base::failbit)`. But should that happen before extracting any characters, or after extracting a weekday string? If it's unspecified, should we say so explicitly?

Can a conforming implementation validate the format string before extracting any characters, and fail early if parsing the actual istream contents can never succeed? Or is `parse("%a", sec)` required to try to parse a valid weekday name before setting `failbit`?

[2023-11-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4950.

1. Modify 30.13 [time.parse] as indicated:

   > -15- All `from_stream` overloads behave as unformatted input functions, except that they have an unspecified effect on the value returned by subsequent calls to `basic_istream<>::is.gcount()`. Each overload takes a format string containing ordinary characters and flags which have special meaning. Each flag begins with a `%`. Some flags can be modified by `E` or `O`. During parsing each flag interprets characters as parts of date and time types according to Table 102. Some flags can be modified by a width parameter given as a positive decimal integer called out as `N` below which governs how many characters are parsed from the stream in interpreting the flag. All characters in the format string that are not represented in Table 102, except for whitespace, are parsed unchanged from the stream. A whitespace character matches zero or more whitespace characters in the input stream.

   > -?- If a format string contains a `%` character that is not part of a flag shown in Table 102, or a modifier that is not allowed for a flag, `is.setstate(ios_base::failbit)` is called. It is unspecified how many characters (if any) are extracted before the call to `is.setstate(ios_base::failbit)`.

   > -16- If a value cannot be determined for the type being parsed from the flags in the format string cannot represent the information that the format flag refers to `is.setstate(ios_base::failbit)` is called. It is unspecified how many characters (if any) are extracted before the call to `is.setstate(ios_base::failbit)`.

   > [Example: A `duration` cannot represent a `weekday` weekday, so `parse("%a", dur)` will always fail if the type of `dur` is a specialization of `duration`. Implementations can check the format string and set `failbit` before extracting any characters. — end example]

### 3962(i). What is the "decimal precision of the input"?

Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2024-01-29

Priority: 3

View other active issues in [time.parse].

View all other issues in [time.parse].

View all issues with New status.

Discussion:

The `%j` flag talks about "the type being parsed" and `%S` talks about "the input time" and "the input", but doesn't really define which types those are.

[2023-11-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4950.

1. Modify 30.13 [time.parse] as indicated:

   > -?- In Table 102 the type `Parsable` denotes the type of the second argument to `from_stream`.

   > `%j` | If the type being parsed `Parsable` is a specialization of `duration`, a decimal number of days.
   > `%S` | The seconds as a decimal number. The modified command `%NS` specifies the maximum number of characters to read. If `N` is not specified, the default is 2 if `Parsable` is a calendrical type (30.8 [time.cal]) if the input time has a precision convertible to seconds. Otherwise the default width is determined by the decimal precision of the input `Parsable`, determined in the same manner as `hh_mm_ss::fractional_width` (30.9 [time.hms]). If the number of fractional decimal digits for the type is zero, then the default for `N` is 2. Otherwise, it is 3 + W, where W is the number of fractional decimal digits. If `N` is less than 3, the field is interpreted as a decimal integer, otherwise and the field is interpreted as a `long double` in a fixed format. If encountered, the locale determines the decimal point character. Leading zeroes are permitted but not required. The modified command `%OS` interprets the locale's alternative representation.

### 3963(i). Different `std::flat_map`/`std::flat_multimap` specializations should be able to share same nested classes

Section: 23.6.8.2 [flat.map.defn], 23.6.9.2 [flat.multimap.defn] Status: New Submitter: Jiang An Opened: 2023-07-24 Last modified: 2023-11-03

Priority: 3

View all other issues in [flat.map.defn].

View all issues with New status.

Discussion:

Per current wording, `std::flat_map<K, V, C1, KCont, VCont>::containers` and `std::flat_multimap<K, V, C2, KCont, VCont>::containers` need to be different types since they are specified as nested classes. Likewise, `std::flat_map<K, V, C, KCont1, VCont1>::value_compare` and `std::flat_multimap<K, V, C, KCont2, VCont2>::value_compare` also need to be different types.

It may be better to allow implementations to make different `std::flat_map`/`std::flat_multimap` specializations share same nested classes.

[2023-11-02; Reflector poll]

Set priority to 3 after reflector poll. "This would mean we have to care about whether the template parameters are associated entities of the `containers` type, for ADL purposes." "That only matters if you're doing silly things, we shouldn't design the library with silly people in mind." "Nobody asked to be able to do this for `value_compare` in associative containers, why do we think vendors would take advantage of this freedom here? NAD."

Proposed resolution:

This wording is relative to N4950.

1. Add the new paragraph after 23.6.8.2 [flat.map.defn] p1 and 23.6.9.2 [flat.multimap.defn] p1 (twice):

   > [Drafting note: The intent is to keep the property that key/mapped types, key/mapped containers, and the comparator type are not the associated types of these nested classes during ADL. ]

   > -?- Implementations may define member types `containers` and `value_compare` as nested classes of another templated class and introduce them as typedef-names. Implementations shall ensure that these nested classes are not themselves class templates and have the specified names.

### 3964(i). `std::atan2` and `std::pow` overloads that take two `std::valarray` parameters should require the arguments to have the same length

Section: 29.6.3.3 [valarray.transcend] Status: New Submitter: Jiang An Opened: 2023-07-28 Last modified: 2023-10-30

Priority: 4

View all issues with New status.

Discussion:

All binary operation between `std::valarray`s expect them to have the same length. But `std::atan2` and `std::pow` lack such Preconditions.

[2023-10-30; Reflector poll]

Set priority to 4 after reflector poll. "Also need to say what those functions return." "Paragraph 1 needs a correction for the functions that takes two operands." "And those are arguments not operands."

Proposed resolution:

This wording is relative to N4950.

1. Modify 29.6.3.3 [valarray.transcend] as indicated:

   >     template<class T> valarray<T> abs (const valarray<T>&);
   >     template<class T> valarray<T> acos (const valarray<T>&);
   >     template<class T> valarray<T> asin (const valarray<T>&);
   >     template<class T> valarray<T> atan (const valarray<T>&);
   >     template<class T> valarray<T> atan2(const valarray<T>&, const valarray<T>&);
   >     template<class T> valarray<T> atan2(const valarray<T>&, const typename valarray<T>::value_type&);
   >     template<class T> valarray<T> atan2(const typename valarray<T>::value_type&, const valarray<T>&);
   >     template<class T> valarray<T> cos (const valarray<T>&);
   >     template<class T> valarray<T> cosh (const valarray<T>&);
   >     template<class T> valarray<T> exp (const valarray<T>&);
   >     template<class T> valarray<T> log (const valarray<T>&);
   >     template<class T> valarray<T> log10(const valarray<T>&);
   >     template<class T> valarray<T> pow (const valarray<T>&, const valarray<T>&);
   >     template<class T> valarray<T> pow (const valarray<T>&, const typename valarray<T>::value_type&);
   >     template<class T> valarray<T> pow (const typename valarray<T>::value_type&, const valarray<T>&);
   >     template<class T> valarray<T> sin (const valarray<T>&);
   >     template<class T> valarray<T> sinh (const valarray<T>&);
   >     template<class T> valarray<T> sqrt (const valarray<T>&);
   >     template<class T> valarray<T> tan (const valarray<T>&);
   >     template<class T> valarray<T> tanh (const valarray<T>&);

   > > -1- Mandates: A unique function with the indicated name can be applied (unqualified) to an operand of type `T`. This function returns a value of type `T` or which can be unambiguously implicitly converted to type `T`.

   > > -?- Preconditions: For `atan2` and `pow` overloads that take two `valarray` parameters, the corresponding array arguments have the same length.

### 3966(i). The `value_type` and `reference` members of `std::flat_(multi)map::(const_)iterator` are unclear

Section: 23.6.8.1 [flat.map.overview], 23.6.9.1 [flat.multimap.overview] Status: New Submitter: Jiang An Opened: 2023-08-04 Last modified: 2023-10-30

Priority: 3

View all other issues in [flat.map.overview].

View all issues with New status.

Discussion:

23.6.8.1 [flat.map.overview] and 23.6.9.1 [flat.multimap.overview] currently only require that the iterator types meet Cpp17InputIterator requirements and model the `std::random_access_iterator` concept.

The intent seems to be that

- the `operator*()` returns proxy references of type `std::pair<const K&, V&>` or `std::pair<const K&, const V&>` (same as the `reference` or `const_reference` types of the container adaptors), and

- the `value_type` of these iterators is `std::pair<K, V>` (same as the `value_type` of the container adaptors).

However, such intent doesn't seem to be clear.

Moreover, given such possibly intended iterator types don't meet Cpp17ForwardIterator requirements, we may need to mention the exceptions in 23.2.2.2 [container.reqmts] or 23.2.7.1 [associative.reqmts.general].

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3967(i). The specification for `std::is_nothrow_*` traits may be ambiguous in some cases involving `noexcept(false)`

Section: 21.3.5.4 [meta.unary.prop] Status: New Submitter: Jiang An Opened: 2023-08-04 Last modified: 2025-10-18

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

`std::is_nothrow_*` traits are currently specified in the form "(some operation) is known not to throw any exceptions". Under the following circumstance, the current specification may be ambiguous:

- a trait detects an explicitly defaulted special member function, and

- the implicit except specification of that function would be `noexcept(true)`, but

- the function is explicitly marked with `noexcept(false)`.

It seems that an implementation may decompose the defaulted special member function and then conclude that the trait should give a `true` result, or may just use the exception specification which gives a `false` result.

Implementors seemingly hold different opinions, see GCC Bug 106611 and this blog post.

Perhaps we may need to clarify that the implementation divergence is permitted, or unambiguously specify these traits to give results consistent with exception specifications.

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

"NAD, this was just a GCC bug."

[2025-10-18; Arthur provides wording]

Proposed resolution:

This wording is relative to N5014.

1. Modify [tab:meta.unary.prop], Table 54 Type property predicates, as indicated:

   Template | Condition | Preconditions
   --- | --- | ---
   ... | ... | ...
       template<class T, class... Args>
       struct is_nothrow_constructible;
   | `is_constructible_v<T, Args...>` is `true` and the variable definition for `is_constructible`, as defined below, contains no potentially-throwing expression (14.5 [except.spec]) is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). | `T` and all types in the template parameter pack `Args` shall be complete types, cv `void`, or arrays of unknown bound.
   ... | ... | ...

### 3968(i). `std::endian::native` value should be more specific about object representations

Section: 22.11.8 [bit.endian] Status: New Submitter: Brian Bi Opened: 2023-08-06 Last modified: 2024-02-22

Priority: 4

View all issues with New status.

Discussion:

22.11.8 [bit.endian] says that "big-endian" and "little-endian" refer to whether bytes are stored in descending or ascending order of significance. In other words, when `std::endian::native` is either `std::endian::big` or `std::endian::little`, we are told something about the object representations o f multi-byte scalar types. However, the guarantee provided in this case is not strong enough to fully specify the object representation, even in the common situation where padding bits are not present. It would be more useful to provide a stronger guarantee.

Consider, for example, if `char` is 8 bits and there is an `uint32_t` type on the current platform. If `std::endian::native` is `std::endian::little`, then the program should be able to rely on the fact that if a `uint32_t` object is copied into an array of 4 `unsigned char`, then the value of the first element of that array actually equals the original value modulo 256. However, because P1236R1 removed the core language specification of the value representation of unsigned integer types, the program cannot actually rely on this. It is conceivable (though unlikely), for example, that `std::endian::native` could be `std::endian::little` but the first byte in a `uint32_t` object is actually the least significant 8 bits flipped, or the least significant 8 bits permuted, or something like that.

[2024-02-22; Reflector poll]

Set priority to 4 after reflector poll in August 2023.

[Jonathan expressed shock that P1236R1 remove portability guarantees that were previously present.]

[Jens explained that no observable guarantees were ever present anyway, which is why Core removed the wording.]

I agree with the thrust of the issue (i.e. the special values for `std::endian` should permit reliance on a particular object representation), but I disagree with the wording chosen. The "pure binary" phrasing that is sort-of defined in a footnote is bad. I think we want to say that all scalar types have no padding bits and that the base-2 representation of an unsigned integer type is formed by the bit concatenation of the base-2 representations of the "unsigned char" values that comprise the object representation of that unsigned integer type. "bit concatenation" should best be phrased in math, e.g. given a value x of some unsigned integer type and the sequence of unsigned char values cj (each having width M) comprising the object representation of x, the coefficients of the base-2 representation of x are xi = c⌊i/M⌋i mod M or somesuch. See 7.6.11 [expr.bit.and] for some phrasing in this area.

Proposed resolution:

This wording is relative to N4950.

1. Modify the 22.11.8 [bit.endian] as indicated; using removed wording from C++17:

   > -2- If all scalar types have size 1 byte, then all of `endian::little`, `endian::big`, and `endian::native` have the same value. Otherwise, `endian::little` is not equal to `endian::big`. If all scalar types are big-endian, `endian::native` is equal to `endian::big`. If all scalar types are little-endian, `endian::native` is equal to `endian::little`. Otherwise, `endian::native` is not equal to either `endian::big` or `endian::little`.`endian::little` is equal to `endian::big` if and only if all scalar types have size 1 byte. If the value representation (6.9 [basic.types]) of every unsigned integer type uses a pure binary numeration systemfootnote ?, then:

   > - If all scalar types have size 1 byte, then `endian::native` is equal to the common value of `endian::little` and `endian::big`.

   > - Otherwise, if all scalar types are big-endian, `endian::native` is equal to `endian::big`.

   > - Otherwise, if all scalar types are little-endian, `endian::native` is equal to `endian::little`.

   > - Otherwise, `endian::native` is not equal to either `endian::big` or `endian::little`.

   > Otherwise, `endian::native` is not equal to either `endian::big` or `endian::little`.

   > > footnote ?) A positional representation for integers that uses the binary digits 0 and 1, in which the values represented by successive bits are additive, begin with 1, and are multiplied by successive integral powers of 2, except perhaps for the bit with the highest position. (Adapted from the American National Dictionary for Information Processing Systems.)

### 3969(i). `std::ranges::fold_left_first_with_iter` should be more ADL-proof

Section: 26.6.18 [alg.fold] Status: New Submitter: Jiang An Opened: 2023-08-10 Last modified: 2023-11-03

Priority: 3

View other active issues in [alg.fold].

View all other issues in [alg.fold].

View all issues with New status.

Discussion:

The following program is currently ill-formed, because 26.6.18 [alg.fold]/10 requires evaluating `*init`, where `init` is an object of an `optional` specialization, which triggers ADL and finds unwanted overloads.

>     #include <algorithm>
>     #include <optional>

>     namespace myns {
>       struct Foo {};

>       void operator*(std::optional<Foo>&);
>       void operator*(const std::optional<Foo>&);
>     }

>     int main()
>     {
>       myns::Foo x[1]{};
>       std::ranges::fold_left_first_with_iter(x, []<class T>(T lhs, T) { return lhs; });
>     }

I think only the member `operator*` overload is intendedly used.

[2023-11-03; Reflector poll]

Many votes for NAD. "Yuck, can we just use `.value()` instead?" "The example is not good motivation, but we should ADL-proof to avoid attempting to complete incomplete associated classes."

Proposed resolution:

This wording is relative to N4950.

1. Modify 26.6.18 [alg.fold] as indicated:

   >     template<input_iterator I, sentinel_for<I> S,
   >              indirectly-binary-left-foldable<iter_value_t<I>, I> F>
   >       requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
   >       constexpr see below ranges::fold_left_first_with_iter(I first, S last, F f);
   >     template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F>
   >       requires constructible_from<range_value_t<R>, range_reference_t<R>>
   >       constexpr see below ranges::fold_left_first_with_iter(R&& r, F f);

   > > -9- Let `U` be

   > > >     decltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))

   > > -10- Effects: Equivalent to:

   > > >     if (first == last)
   > > >       return {std::move(first), optional<U>()};
   > > >     optional<U> init(in_place, *first);
   > > >     for (++first; first != last; ++first)
   > > >       *initinit.operator*() = invoke(f, std::move(*initinit.operator*()), *first);
   > > >     return {std::move(first), std::move(init)};

### 3972(i). Issues with `join_with_view::iterator`'s `iter_swap`

Section: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: Hewill Kang Opened: 2023-09-04 Last modified: 2023-11-03

Priority: 2

View other active issues in [range.join.with.iterator].

View all other issues in [range.join.with.iterator].

View all issues with New status.

Discussion:

The `iter_swap` customization for `join_with_view::iterator` allows swapping iterators with different element types, potentially leading to unsafe behavior, for example:

>     vector<vector<string>> x{{"a"}, {"b"}, {"c"}};
>     vector<string_view>    y{"-"};
>     auto r = x | views::join_with(y);
>     auto i = r.begin();
>     auto j = ranges::next(i);
>     ranges::iter_swap(j, i);
>     for (auto&& elem : r)
>       cout << elem << " "; // AddressSanitizer: stack-use-after-return on address

The above swaps two iterators whose reference are `string_view&` and `string&` respectively, which ultimately results in `string_view` being referenced to a local variable and left dangling.

[2023-11-02; Reflector poll]

Set priority to 2 after reflector poll.

Proposed resolution:

### 3976(i). What does it mean for a type to be "allocator aware"?

Section: 23.2.2.5 [container.alloc.reqmts] Status: New Submitter: Alisdair Meredith Opened: 2023-08-14 Last modified: 2025-10-16

Priority: 4

View all other issues in [container.alloc.reqmts].

View all issues with New status.

Discussion:

Trying to interpret the specification for allocator-aware containers, 23.2.2.5 [container.alloc.reqmts].

P1 establishes that `std::array` is not an allocator-aware container, but all other containers in the standard are.

P2 then adds vocabulary, with ruling that if a container type `X` is not allocator-aware then the specification is as-if the allocator were `std::allocator<T>`. I cannot find a specification for allocator-aware, and do not believe we should treat this as a recursive definition, as the only standard container that would be affected would be `std::array`, which absolutely does not want to apply all the terms in this subclause.

It looks like we might mean types that do not have everything specified by the first sentence, such as an `allocator_type` member. Perhaps we want to say a container type `X` is allocator-aware using allocator `A` if […], and then that `basic_string` and containers that are not otherwise allocator aware are are treated as-if they were allocator-aware using `std::allocator<typename X::value_type>` (where `value_type` is already guaranteed to exist by the container requirements, 23.2.2.2 [container.reqmts] p1).

[2025-10-16; Reflector poll]

Set priority to 4 after reflector poll.

"NAD. 'If X is not allocator-aware' is equivalent to 'if X is not an allocate-aware container type'. Note that 23.2.2.5 [container.alloc.reqmts]/2 is not defining the requirements on an allocator-aware container; it's defining the related Cpp17Meow requirements. The allocator-aware container requirements are defined in paragraphs 3 and later, so this isn't a recursive definition."

Proposed resolution:

### 3977(i). `constexpr` and `noexcept` for operators for bitmask types

Section: 16.3.3.3.3 [bitmask.types] Status: New Submitter: Jiang An Opened: 2023-08-19 Last modified: 2024-02-22

Priority: 3

View other active issues in [bitmask.types].

View all other issues in [bitmask.types].

View all issues with New status.

Discussion:

Currently, no operator in 16.3.3.3.3 [bitmask.types]/2 is specified as `noexcept`, and the compound assignment operators are not specified as `constexpr`.

Implementations are divergent on this. E.g., MSVC STL consistently marks them `constexpr` and `noexcept` (given MSVC STL doesn't support pre-C++14 modes), while libstdc++'s compound assignment operators for `match_flag_type` are `constexpr` since C++14 but lack `noexcept`, and the operators for `launch` are `noexcept` but not `constexpr`.

I think it's better to ensure more consistency be integer types and non-integer bitmask types, i.e., require the compound assignment operators to be `constexpr` (only available in C++14 and later) and all operators to be `noexcept`.

[2024-02-22; Reflector poll]

Set priority to 3 after reflector poll in September 2023.

[Jonathan commented]

"The proposed change only affects an example showing a possible way to implement a made-up example type. It doesn't change any requirements on bitmask types, or change anything for any of the bitmask types defined in the standard library. It doesn't say that implementing them without noexcept and constexpr would be invalid. This change has no normative effect and certainly doesn't achieve the stated aim of requiring these assignments to be constexpr and non-throwing."

[Casey agreed]

"We should strike paragraph two completely and write up the actual requirements that a bitmask type is required to meet, but that's a lot of work for someone."

Proposed resolution:

This wording is relative to N4958.

1. Modify 16.3.3.3.3 [bitmask.types] as indicated:

   > -2- The bitmask type `bitmask` can be written:

   > >     // For exposition only.
   > >     // int_type is an integral type capable of representing all values of the bitmask type.
   > >     enum bitmask : int_type {
   > >       V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, …
   > >     };

   > >     inline constexpr bitmask C0(V0);
   > >     inline constexpr bitmask C1(V1);
   > >     inline constexpr bitmask C2(V2);
   > >     inline constexpr bitmask C3(V3);

   > >     […]

   > >     constexpr bitmask operator&(bitmask X, bitmask Y) noexcept {
   > >       return static_cast<bitmask>(
   > >         static_cast<int_type>(X) & static_cast<int_type>(Y));
   > >     }
   > >     constexpr bitmask operator|(bitmask X, bitmask Y) noexcept {
   > >       return static_cast<bitmask>(
   > >         static_cast<int_type>(X) | static_cast<int_type>(Y));
   > >     }
   > >     constexpr bitmask operator^(bitmask X, bitmask Y) noexcept {
   > >       return static_cast<bitmask>(
   > >         static_cast<int_type>(X) ^ static_cast<int_type>(Y));
   > >     }
   > >     constexpr bitmask operator~(bitmask X) noexcept {
   > >       return static_cast<bitmask>(~static_cast<int_type>(X));
   > >     }
   > >     constexpr bitmask& operator&=(bitmask& X, bitmask Y) noexcept {
   > >       X = X & Y; return X;
   > >     }
   > >     constexpr bitmask& operator|=(bitmask& X, bitmask Y) noexcept {
   > >       X = X | Y; return X;
   > >     }
   > >     constexpr bitmask& operator^=(bitmask& X, bitmask Y) noexcept {
   > >       X = X ^ Y; return X;
   > >     }

### 3983(i). `ranges::to` adaptors are underconstrained

Section: 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Hewill Kang Opened: 2023-08-23 Last modified: 2023-11-03

Priority: 3

View all issues with New status.

Discussion:

The `ranges::to` adaptor returns a range adaptor closure object that stores a copy of the decayed parameter pack.

However, the function signature does not constrain the type of arguments, which leads to hard errors within the function body when they cannot be copied (demo):

>     #include <ranges>
>     #include <vector>

>     struct NonMovable {
>       NonMovable() = default;
>       NonMovable(NonMovable&&) = delete;
>     };

>     int main() {
>       auto r = std::ranges::to<std::vector>(NonMovable{}); // hard error in MSVC-STL and libc++
>     }

Previous resolution [SUPERSEDED]

This wording is relative to N4958.

1. Modify 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     #include <compare>              // see [compare.syn]
   >     #include <initializer_list>     // see [initializer.list.syn]
   >     #include <iterator>             // see [iterator.synopsis]

   >     namespace std::ranges {
   >       […]
   >       // 25.5.7 [range.utility.conv], range conversions
   >       template<class C, input_range R, class... Args> requires (!view<C>>)
   >         constexpr C to(R&& r, Args&&... args);                                          // freestanding
   >       template<template<class...> class C, input_range R, class... Args>
   >         constexpr auto to(R&& r, Args&&... args);                                       // freestanding
   >       template<class C, class... Args>
   >         requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...)
   >         constexpr auto to(Args&&... args);                                              // freestanding
   >       template<template<class...> class C, class... Args>
   >         requires (constructible_from<decay_t<Args>, Args> && ...)
   >         constexpr auto to(Args&&... args);                                              // freestanding
   >       […]
   >     }

2. Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:

   >     template<class C, class... Args>
   >       requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...)
   >       constexpr auto to(Args&&... args);
   >     template<template<class...> class C, class... Args>
   >       requires (constructible_from<decay_t<Args>, Args> && ...)
   >       constexpr auto to(Args&&... args);

   > > -1- Mandates: For the first overload, `C` is a cv-unqualified class type.

   > > -2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object]) `f` that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:

   > > […]

[2023-11-03; Reflector poll]

Set priority to 3 after reflector poll. Votes split between NAD and using Mandates instead of constraints.

[2023-09-28; Hewill provides alternative wording]

The new wording form is consistent with the current wording, that is, it has a similar structure with 25.7.2 [range.adaptor.object] p1 and 25.7.2 [range.adaptor.object] p8.

Proposed resolution:

This wording is relative to N4958.

1. Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:

   >     template<class C, class... Args> requires (!view<C>)
   >       constexpr auto to(Args&&... args);
   >     template<template<class...> class C, class... Args>
   >       constexpr auto to(Args&&... args);

   > > -1- Mandates: For the first overload, `C` is a cv-unqualified class type.

   > > -2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object]) `f` that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:

   > > 1. (2.1) — It has no target object.

   > > 2. (2.2) — Its bound argument entities `bound_args` consist of objects of types `decay_t<Args>...` direct-non-list-initialized with `std::forward<Args>(args)...`, respectively.

   > > 3. (2.3) — Its call pattern is `to<C>(r, bound_args...)`, where `r` is the argument used in a function call expression of `f`.

   > > -?- Remarks: The expression `to(args...)` is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.

### 3985(i). `ranges::to` should Mandates `C` not to be view

Section: 25.5.7.2 [range.utility.conv.to], 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Hewill Kang Opened: 2023-08-25 Last modified: 2023-10-30

Priority: 3

View other active issues in [range.utility.conv.to].

View all other issues in [range.utility.conv.to].

View all issues with New status.

Discussion:

In order to solve issues in LWG 3787(i) and LWG 3847(i) that the template parameter `C` can be specified as an unreasonable type, `ranges::to` adds a Mandates that requires `C` to be a cv-unqualified class type.

However, the earliest requirement that `C` not be a view was still imposed by the constraints of function signatures, although it is unclear why Constraints were used in the first place, such a way of kicking out the function candidate does bring undesirable hard errors and poor diagnostics (demo):

>     #include <ranges>

>     int main() {
>       auto iota = std::views::iota(0, 10);
>       auto take = std::ranges::to<std::ranges::take_view>(iota, 5);  // hard error in function body
>       auto drop = iota | std::ranges::to<std::ranges::drop_view>(5); // poor diagnostics
>     }

I think consistent use of Mandates for template parameter `C` is more appropriate, as `static_assert` provide clearer and more readable diagnostics.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4958.

1. Modify 25.2 [ranges.syn] as indicated:

   >     #include <compare>              // see 17.12.1 [compare.syn]
   >     #include <initializer_list>     // see 17.11.2 [initializer.list.syn]
   >     #include <iterator>             // see 24.2 [iterator.synopsis]

   >     namespace std::ranges {
   >       […]
   >       // 25.5.7 [range.utility.conv], range conversions
   >       template<class C, input_range R, class... Args> requires (!view<C>)
   >         constexpr C to(R&& r, Args&&... args);                                          // freestanding
   >       template<template<class...> class C, input_range R, class... Args>
   >         constexpr auto to(R&& r, Args&&... args);                                       // freestanding
   >       template<class C, class... Args> requires (!view<C>)
   >         constexpr auto to(Args&&... args);                                              // freestanding
   >       template<template<class...> class C, class... Args>
   >         constexpr auto to(Args&&... args);                                              // freestanding
   >       […]
   >     }

2. Modify 25.5.7.2 [range.utility.conv.to] as indicated:

   >     template<class C, input_range R, class... Args> requires (!view<C>)
   >       constexpr C to(R&& r, Args&&... args);

   > > -1- Mandates: `C` is a cv-unqualified class type and does not satisfy `view`.

   > > […]

3. Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:

   >     template<class C, class... Args> requires (!view<C>)
   >       constexpr auto to(Args&&... args);
   >     template<template<class...> class C, class... Args>
   >       constexpr auto to(Args&&... args);

   > > -1- Mandates: For the first overload, `C` is a cv-unqualified class type and does not satisfy `view`.

   > > […]

### 3986(i). `basic_const_iterator` doesn't work with `optional`

Section: 24.5.3 [const.iterators] Status: New Submitter: Hewill Kang Opened: 2023-08-26 Last modified: 2023-10-30

Priority: 3

View other active issues in [const.iterators].

View all other issues in [const.iterators].

View all issues with New status.

Discussion:

The heterogeneous comparison operators of `basic_const_iterator` only constrain type `I` to be comparable with the underlying iterator type, which allows non-iterator types such as `optional`, and the heterogeneous comparison operators of the latter would make the situation worse (demo):

>     #include <iterator>
>     #include <optional>

>     int main() {
>       std::basic_const_iterator<const int*> it;
>       std::optional opt{it};
>       auto c = opt < it; // infinite meta-recursion
>       auto d = it - opt; // infinite meta-recursion
>     }

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 3988(i). Should `as_const_view` and `basic_const_iterator` provide `base()`?

Section: 24.5.3 [const.iterators], 25.7.22.2 [range.as.const.view] Status: Open Submitter: Hewill Kang Opened: 2023-08-28 Last modified: 2024-03-15

Priority: 3

View other active issues in [const.iterators].

View all other issues in [const.iterators].

View all issues with Open status.

Discussion:

Currently, both `as_const_view` and `basic_const_iterator` provide `base()` members to return the underlying range and iterator, which seems to expose vulnerabilities in modifying them:

>     #include <vector>
>     #include <ranges>

>     int main() {
>       std::vector v{1, 2, 3};

>       auto f = [](std::span<int>::const_iterator i) {
>         *i.base() = 4;
>       };
>       f(std::span{v}.cbegin());

>       auto g = [](const std::ranges::constant_range auto& r) {
>         r.begin().base()[1] = 5;
>         r.base()[2] = 6;
>       };
>       g(std::ranges::as_const_view(v));

>       // v now becomes [4, 5, 6]
>     }

I don't think such a shortcut should be provided as it doesn't seem to be the intention and could be harmful.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. Send to SG9.

[Kona 2023-11-07; move to Ready]

[2024-03-15; move back to Open following LEWG feedback]

SG9 approved the proposed change but then LEWG had no consensus for change. LWG should revisit in Tokyo.

Proposed resolution:

This wording is relative to N4958.

1. Modify 24.5.3.3 [const.iterators.iterator], class template `basic_const_iterator` synopsis, as indicated:

   >     namespace std {
   >       […]

   >       template<input_iterator Iterator>
   >       class basic_const_iterator {
   >         […]
   >         constexpr const Iterator& base() const & noexcept;
   >         constexpr Iterator base() &&;
   >         […]
   >       };
   >     }

2. Modify 24.5.3.5 [const.iterators.ops] as indicated:

   >     constexpr const Iterator& base() const & noexcept;

   > > -4- Effects: Equivalent to: `return current_;`

   >     constexpr Iterator base() &&;

   > > -5- Effects: Equivalent to: `return std::move(current_);`

3. Modify 25.7.22.2 [range.as.const.view] as indicated:

   >     namespace std::ranges {
   >       template<view V>
   >         requires input_range<V>
   >       class as_const_view : public view_interface<as_const_view<V>> {
   >         […]
   >         constexpr V base() const & requires copy_constructible<V> { return base_; }
   >         constexpr V base() && { return std::move(base_); }
   >         […]
   >       };
   >     }

### 3989(i). The whole range for an iterator obtained from a `std::span` or `std::basic_string_view` is not clear

Section: 27.3 [string.view], 23.7.2.2 [views.span] Status: New Submitter: Jiang An Opened: 2023-08-29 Last modified: 2023-10-30

Priority: 3

View other active issues in [string.view].

View all other issues in [string.view].

View all issues with New status.

Discussion:

It is unclear whether the following program has undefined behavior:

>     #include <cassert>
>     #include <span>
>     #include <string_view>

>     int main()
>     {
>       int arr[2]{42, 84};
>       std::span<int> sp1{arr, 1};
>       std::span<int> sp2{arr + 1, 1};

>       assert(sp2.begin() - sp1.begin() == 1); // Is this well-defined?
>       assert(sp2.begin() == sp1.end());       // ditto
>       assert(*sp1.end() == 84);               // ditto

>       const char str[]{"string"};
>       std::string_view sv1{str, 3};
>       std::string_view sv2{str + 3, 3};

>       assert(sv2.begin() - sv1.begin() == 3); // Is this well-defined?
>       assert(sv2.begin() == sv1.end());       // ditto
>       assert(*sv1.end() == 'i');              // ditto
>     }

Currently MSVC STL strictly diagnose the arithmetic between different `span`s/`basic_string_view`s, even the views are on the same underlying range (see this Github issue). libstdc++ and libc++ silently accept these operations.

Perhaps the standard should clarify what the whole ranges are when determining whether operations between iterators from different views on the same underlying range is well-defined.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. Libc++ diagnoses the example with the right macros defined. "Should `substr` and `remove_suffix` tighten the bounds or copy them from the original view?"

Proposed resolution:

### 3991(i). `variant`'s move assignment should not be guaranteed to produce a valueless by exception state

Section: 22.6.3.4 [variant.assign] Status: New Submitter: Brian Bi Opened: 2023-08-29 Last modified: 2023-10-30

Priority: 3

View other active issues in [variant.assign].

View all other issues in [variant.assign].

View all issues with New status.

Discussion:

22.6.3.4 [variant.assign] bullet 8.4 states that an alternative-changing move assignment on `std::variant` is equivalent to a call to `emplace`. However, bullet 10.1 also states that if the construction of the new alternative exits via an exception, then the destination of the assignment is guaranteed to become valueless by exception. This is inconsistent with the specification of `emplace`, 22.6.3.5 [variant.mod]/11, which permits (but does not require) the variant to become valueless.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. "The remark is normatively redundant with the spec of `emplace`, strike it."

Proposed resolution:

This wording is relative to N4958.

1. Modify 22.6.3.4 [variant.assign] as indicated:

   >     constexpr variant& operator=(variant&& rhs) noexcept(see below);

   > > -6- Let `j` be `rhs.index()`.

   > > […]

   > > -8- Effects:

   > > 1. (8.1) — If neither `*this` nor `rhs` holds a value, there is no effect.

   > > 2. (8.2) — Otherwise, if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value.

   > > 3. (8.3) — Otherwise, if `index() == j`, assigns `get<j>(std::move(rhs))` to the value contained in `*this`.

   > > 4. (8.4) — Otherwise, equivalent to `emplace<j>(get<j>(std::move(rhs)))`.

   > > […]

   > > -10- Remarks: […]

   > > 1. (10.1) — If an exception is thrown during the call to `Tj`'s move construction (with `j` being `rhs.index()`), the `variant` will hold no value.

   > > 2. (10.2) — If an exception is thrown during the call to `Tj`'s move assignment, the state of the contained value is as defined by the exception safety guarantee of `Tj`'s move assignment; `index()` will be `j`.

### 3993(i). The `parse` function of a BasicFormatter type needs to be `constexpr`

Section: 28.5.6.1 [formatter.requirements] Status: New Submitter: Jiang An Opened: 2023-10-10 Last modified: 2023-10-30

Priority: 3

View all other issues in [formatter.requirements].

View all issues with New status.

Discussion:

Format strings need to be checkable at compile-time since P2216R3. In order to check a compile-time format string, the `parse` functions of the formatters need to be called. However, currently there is seemingly no requirement for the `parse` function to be `constexpr`, and hence whether a format string is correct for formatted types may be not well-defined at compile-time.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. "Should be OK to provide a non-constexpr `parse` if you only use it with `std::vformat` and not `std::format`." "Only the `std::formatter` specializations in the standard library should be required to have constexpr `parse`." "No need to require this, but a note with this text explaining when a call to `parse` can be a constant expression might be nice."

Proposed resolution:

This wording is relative to N4958.

1. Modify 28.5.6.1 [formatter.requirements], Table 73, BasicFormatter requirements [tab:formatter.basic] as indicated:

   > Table 73: BasicFormatter requirements [tab:formatter.basic]
   > Expression | Return type | Requirement
   > --- | --- | ---
   > `g.parse(pc)` | `PC::iterator` | Parses format-spec (28.5.2 [format.string]) for type `T` in the range `[pc.begin(), pc.end())` until the first unmatched character. Throws `format_error` unless the whole range is parsed or the unmatched character is `}`. [Note 1: This allows formatters to emit meaningful error messages. — end note] Stores the parsed format specifiers in `*this` and returns an iterator past the end of the parsed range. The expression shall be a core constant expression if `g`, `pc.begin()`, `pc.end()`, and lvalue-to-rvalue conversion for each element in `[pc.begin(), pc.end())` are core constant expressions, and no exception is thrown.
   > `…`

### 3994(i). `adaptor(args...)(r)` is not equivalent to `std::bind_back(adaptor, args...)(r)`

Section: 25.7.2 [range.adaptor.object] Status: New Submitter: Hewill Kang Opened: 2023-10-11 Last modified: 2023-11-03

Priority: 4

View all other issues in [range.adaptor.object].

View all issues with New status.

Discussion:

25.7.2 [range.adaptor.object] p8 specifies that:

> The expression `adaptor(args...)` produces a range adaptor closure object `f` that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:

According to the subsequent description, it can be inferred that the behavior is similar to `std::bind_back(adaptor, args...)` which also returns a perfect forwarding call wrapper.

Among them, "A perfect forwarding call wrapper is an argument forwarding call wrapper that forwards its state entities to the underlying call expression" according to [func.require]/4, and call wrapper in [func.require]/3 is described as:

> Every call wrapper (22.10.3 [func.def]) meets the Cpp17MoveConstructible and Cpp17Destructible requirements.

In order to conform with the specification, standard functions that return perfect forwarding call wrappers such as `std::bind_front/back` and `std::not_fn` all Mandates that `(is_constructible_v<BoundArgs, Args> && ...)` and `(is_move_constructible_v<BoundArgs> && ...)` are each `true`, the former condition corresponds to 25.7.2 [range.adaptor.object] p8:

> The expression `adaptor(args...)` is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.

However, the latter does not have a corresponding description in `<ranges>`. In other words, range adaptor objects do not explicitly indicate that the bound argument must be move-constructible. This results in implementation divergence for some uncommon types (demo):

>     #include <ranges>
>     #include <string_view>

>     constexpr struct WeirdFive {
>       WeirdFive() = default;
>       WeirdFive(const WeirdFive&) = default;
>       constexpr operator int() const { return 5; }

>       WeirdFive(WeirdFive&&) = delete;
>     } five;

>     constexpr std::string_view sv{"hello"};
>     static_assert(sv == std::views::take(five)(sv)); // libstdc++/libc++ reject, MSVC-STL accepts

Above, libstdc++ always moves arguments into internal members, which leads to hard errors in the member initializer list; libc++ uses `std::bind_back` for argument binding, which also leads to hard errors in the function body as the former requires arguments to be move-constructible; MSVC-STL is the most compliant with current wording.

[2023-11-02; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

### 3995(i). Issue with custom index conversion in `<mdspan>`

Section: 23.7.3 [views.multidim] Status: New Submitter: Hewill Kang Opened: 2023-10-11 Last modified: 2023-10-30

Priority: 3

View all issues with New status.

Discussion:

Currently, `std::layout_meow::mapping::operator()` has the following definition (23.7.3.4.5.3 [mdspan.layout.left.obs]):

>     template<class... Indices>
>       constexpr index_type operator()(Indices... i) const noexcept;

> > -2- Constraints:

> > 1. (2.1) — `sizeof...(Indices) == extents_type::rank()` is `true`,

> > 2. (2.2) — `(is_convertible_v<Indices, index_type> && ...)` is `true`, and

> > 3. (2.3) — `(is_nothrow_constructible_v<index_type, Indices> && ...)` is `true`.

> > Preconditions: `extents_type::index-cast(i)` is a multidimensional index in `extents_` (23.7.3.1 [mdspan.overview]).

> > Effects: Let `P` be a parameter pack such that

> > >     is_same_v<index_sequence_for<Indices...>, index_sequence<P...>>

> > is `true`. Equivalent to:

> > >     return ((static_cast<index_type>(i) * stride(P)) + ... + 0);

Above, `is_convertible_v<Indices, index_type>` implies that `index_type` can be constructed through rvalue-qualified conversion operators. However, we cast the lvalue `i` in the return statement, which makes the expression possibly ill-formed. The same goes for `extents_type::index-cast(i)`.

However, if we use `std::move` before casting, this will result in the rvalue-qualified conversion operator being called in Preconditions via `extents_type::index-cast(i)` before the mapping index is actually calculated, so that the expression may no longer be valid. And such an issue already exists in `mdspan::operator[]`.

In addition, the variadic version of `mdspan::operator[]` constraints `is_convertible_v<OtherIndexTypes, index_type>`, but its `array`/`span` version constraints `is_convertible_v<const OtherIndexType&, index_type>`.

This seems to bring inconsistency as `mdspan[arr]` may not necessarily guarantee `mdspan[arr[i]...]`.

I think we should unanimously require that custom indexes can be converted to `index_type` via `const` lvalue references, which eliminates the worry of conversion expiration.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. "P4 - doesn't affect 'normal' uses of custom index types. Only affects expert users that interface with the mapping directly, because `mdspan` does the conversions."

Proposed resolution:

### 3997(i). `std::formatter` specializations should be consistently restricted to supported character types

Section: 28.5.1 [format.syn], 30.2 [time.syn], 32.4.3.2 [thread.thread.id] Status: New Submitter: Jiang An Opened: 2023-10-15 Last modified: 2023-11-04

Priority: 4

View all other issues in [format.syn].

View all issues with New status.

Discussion:

28.5.6.4 [format.formatter.spec]/2 requires some `std::formatter` specializations are only enabled for character types `char` and `wchar_t`. But for specializations that are explicitly shown in the synopses (28.5.1 [format.syn], 30.2 [time.syn], and 32.4.3.2 [thread.thread.id]), there is no such restrictions for `charT`. The difference may be observable via the `std::formattable` concept, e.g. `std::formattable<std::chrono::second, char8_t>` is currently satisfied, even though there would be a hard error during formatting due to `STATICALLY-WIDEN`.

Perhaps we should introduce an exposition-only concept that is only satisfied (and modeled) by `char` and `wchar_t` for now, and constrain the `charT` template parameter of all `std::formatter` specializations that are explicitly mentioned in synopses with that concept. This is what libc++ currently implements (e.g. the `__fmt_char_type` concept and its usage in `<chrono>`).

[2023-11-03; Reflector poll]

Set priority to 4 after reflector poll. "Problem looks theoretical, all entry points to formatters are constrained."

Proposed resolution:

### 3998(i). Constants in `std::regex_constants` should be allowed to be enumerators

Section: 28.6.4 [re.const] Status: New Submitter: Jiang An Opened: 2023-10-18 Last modified: 2024-02-22

Priority: 3

View all other issues in [re.const].

View all issues with New status.

Discussion:

Currently, MSVC STL and libc++ implement constants in `std::regex_constants` as enumerators of unscoped enumerations, while the standard specify them to be `constexpr` variables.

Casey Carter argued that it would be better to make these constants prvalues to avoid wasting space in object files. And presumably almost all users just use their values without odr-using them or relying on their value category.

Perhaps we should allow these constants to be enumerators of the corresponding type (`syntax_option_type`, `match_flag_type`, or `error_type`) if the corresponding type is an enumeration.

Notes: since C++20, we can even make the enumeration types scoped and expose the enumerators to the namespace by using-declaration or using-enum-declaration. P0439R0 might be related, since it changed former enumerators to `constexpr` variables.

[2024-02-22; Reflector poll]

Set priority to 3 after reflector poll in October 2023.

[Jonathan commented]

"Why only change the `regex_constants` bitmask constants, and not those for `ctype_base::mask`, `ios_base::fmtflags`, `ios_base::iostate`, and `ios_base::openmode`?"

Proposed resolution:

### 3999(i). P0439R0 changed the value category of memory order constants

Section: 32.5.4 [atomics.order] Status: New Submitter: Jiang An Opened: 2023-10-18 Last modified: 2023-11-03

Priority: 4

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with New status.

Discussion:

P0439R0 made `std::memory_order` an scoped enumeration type. However, it also changed former enumerators (`std::memory_order_seq_cst`) to `constexpr` variables.

It is unclear the additional change is intended while it presumably bothered nobody in practice. Given there is `using enum` (P1099R5) in C++20, it may be better to keep these constants being prvalues.

[2023-11-02; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

This wording is relative to N4964.

1. Modify 32.5.2 [atomics.syn], header `<atomic>` synopsis, as indicated:

   >     namespace std {
   >       // 32.5.4 [atomics.order], order and consistency
   >       enum class memory_order : unspecified;                        // freestanding
   >       inline constexpr memory_order memory_order_relaxed = memory_order::relaxedusing memory_order::memory_order_relaxed; // freestanding
   >       inline constexpr memory_order memory_order_consume = memory_order::consumeusing memory_order::memory_order_consume; // freestanding
   >       inline constexpr memory_order memory_order_acquire = memory_order::acquireusing memory_order::memory_order_acquire; // freestanding
   >       inline constexpr memory_order memory_order_release = memory_order::releaseusing memory_order::memory_order_release; // freestanding
   >       inline constexpr memory_order memory_order_acq_rel = memory_order::acq_relusing memory_order::memory_order_acq_rel; // freestanding
   >       inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cstusing memory_order::memory_order_seq_cst; // freestanding

   >       […]
   >     }
   >     […]

2. Modify 32.5.4 [atomics.order] as indicated:

   >     namespace std {
   >       enum class memory_order : unspecified {
   >         relaxed, consume, acquire, release, acq_rel, seq_cst,
   >         memory_order_relaxed = relaxed, memory_order_consume = consume, memory_order_acquire = acquire,
   >         memory_order_release = release, memory_order_acq_rel = acq_rel, memory_order_seq_cst = seq_cst
   >       };
   >     }

### 4002(i). The definition of `iota_view::iterator::iterator_concept` should be improved

Section: 25.6.4.3 [range.iota.iterator] Status: New Submitter: Hewill Kang Opened: 2023-10-27 Last modified: 2023-11-07

Priority: 3

View other active issues in [range.iota.iterator].

View all other issues in [range.iota.iterator].

View all issues with New status.

Discussion:

When `W` models `incrementable`, the `iota_view` iterator will be a `forward_iterator`, which accidentally makes `iota_view<istream_iterator<int>>` satisfies `forward_range`, which is obviously incorrect.

Since iterator types are well within the domain of `W`, it seems more appropriate and accurate to define the `iterator_concept` through the iterator concept in this case.

[2023-11-07; Reflector poll]

Set priority to 3 after reflector poll. "This would change the iterator concept for types which model `advanceable` but do not model `random_access_iterator`. A type with += and -= and operator* will currently get `random_access_iterator_tag` but this proposed resolution would change it to `bidirectional_iterator_tag`." "If we want to handle this case (and not leave it as IFNDR - the type satisfies-but-not-models `incrementable`) then we need to account for it everywhere we are currently using `incrementable` (and possibly `equality_comparable`?), and not just in the concept."

Proposed resolution:

This wording is relative to N4964.

1. Modify 25.6.4.3 [range.iota.iterator] as indicated:

   > [Drafting note: When `W` only models `input_or_output_iterator`, it implies that its pre- and post-increment are not equality-preserving, so `iterator_concept` should be `input_iterator_tag`.]

   > -1- `iterator::iterator_concept` is defined as follows:

   > 1. (?.?) — If `W` models `input_or_output_iterator`, then

   >    1. (?.?) — if `W` models `random_access_iterator`, then `iterator_concept` is `random_access_iterator_tag`;

   >    2. (?.?) — otherwise, if `W` models `bidirectional_iterator`, then `iterator_concept` is `bidirectional_iterator_tag`;

   >    3. (?.?) — otherwise, if `W` models `forward_iterator`, then `iterator_concept` is `forward_iterator_tag`;

   >    4. (?.?) — otherwise, `iterator_concept` is `input_iterator_tag`.

   > 2. (1.1) — Otherwise, iIf `W` models `advanceable`, then `iterator_concept` is `random_access_iterator_tag`.

   > 3. (1.2) — Otherwise, if `W` models `decrementable`, then `iterator_concept` is `bidirectional_iterator_tag`.

   > 4. (1.3) — Otherwise, if `W` models `incrementable`, then `iterator_concept` is `forward_iterator_tag`.

   > 5. (1.4) — Otherwise, `iterator_concept` is `input_iterator_tag`.

### 4004(i). The load and store operation in §[atomics.order] p1 is ambiguous

Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2023-10-30 Last modified: 2024-05-19

Priority: 3

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with SG1 status.

Discussion:

32.5.4 [atomics.order] p1 says:

> 1. (1.2) — `memory_order::release`, `memory_order::acq_rel`, and `memory_order::seq_cst`: a store operation performs a release operation on the affected memory location.

> 2. (1.3) — `memory_order::consume`: a load operation performs a consume operation on the affected memory location. […]

> 3. (1.4) — `memory_order::acquire`, `memory_order::acq_rel`, and `memory_order::seq_cst`: a load operation performs an acquire operation on the affected memory location.

What do the store and load operations intend to mean in this context? If there is no extra specification, it is easy to consider them as the operations performed by the non-static member functions "store" and "load" defined in the `atomic` class (template).

32.5.4 [atomics.order] p2 says

> An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.

According to the above interpretation, A is an operation performed by the non-static member function `store`, however, I think the following example can establish the synchronization relationship

>     std::atomic<int> x{0};

>     Thread 1:
>     int expected = 0;
>     x.compare_exchange_strong(expected, 1, memory_order::release, memory_order::relaxed); // #1

>     Thread 2:
>     int expected = 1;
>     while(x.compare_exchange_strong(expected, 2, memory_order::acquire, memory_order::relaxed)){} // #2

Assuming the RMW operations are successful in the two threads, I think `#1` intends to perform a release operation while `#2` performs an acquire operation, and hence they can establish the synchronization relationship, however, they both are RMW operations.

It should be clearly defined which are store operations and which are load operations.

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll in November 2023. Ask SG1 to look.

Jonathan: "Interpreting this to only mean the `store` and `load` member functions wouldn't even be self-consistent. Could be clearer though, 6.10.2 [intro.multithread] talks about reads and writes (and RMW ops) and only uses "store" and "load" informally. Maybe just add something saying "reads are also called loads and writes are also called stores".

[2024-05-15; jim x comments and expands the discussion]

This is an addition to this issue, consider this example:

>     std::atomic<int> x{0};

>     Thread 1:
>     int expected = 0;
>     x.compare_exchange_strong(expected, 1, memory_order::acq_rel, memory_order::relaxed); // #1

>     Thread 2:
>     int expected = 1;
>     while(x. compare_exchange_strong(expected, 2, memory_order::acq_rel, memory_order::relaxed)){} // #2

`memory_order::acq_rel` performs a release operation when the operation is a store and is an acquire operation when the operation is a load. It is unclear what operations `#1` and `#2` are considered when they succeed, as pointed out in the original issue, we still don't specify whether RMW is classified as load or store operation or can be both.

We should specify how the `success` order affects the RMW when it is viewed as a load or store operation.

Proposed resolution:

### 4005(i). "Required behavior" too narrowly defined

Section: 3.49 [defns.required.behavior], 16.3.2.4 [structure.specifications], 16.4.5.8 [res.on.functions] Status: New Submitter: Eric Niebler Opened: 2023-11-03 Last modified: 2024-06-24

Priority: 2

View all issues with New status.

Discussion:

The library's definition of the term "required behavior" (3.49 [defns.required.behavior]) makes it specific to certain replaceable functions such as the replaceable global allocation functions. Notably, it is not one of the elements that are allowed to appear in the descriptions of general function semantics. That is, it isn't in the list in 16.3.2.4 [structure.specifications] p3.

However, the specification of the random number generator library uses "Required behavior" as such a descriptive element of its functions' semantics (e.g., 29.5.6 [rand.predef]).

I think that's a fine use of "Required behavior", so I would like the term to be more generally applicable to the behavior of any stdlib function that may be customized by users. This is relevant for `std::execution`, where algorithms are customizable but the customizations are required to behave a certain way.

Daniel:

Bullet (2.4) of 16.4.5.8 [res.on.functions] also contradicts to the more narrow definition of 3.49 [defns.required.behavior] by suddenly extending it to "destructor operations".

[2024-03-11; Reflector poll]

Set priority to 2 after reflector poll.

[2024-06-24; The approval of P2810R4 means that "required behavior" is now being incorrectly used in the working paper.]

Proposed resolution:

### 4007(i). Mystic prohibition of calling a `volatile`-qualified perfect forwarding call wrapper

Section: 22.10.4 [func.require] Status: New Submitter: Jiang An Opened: 2023-11-05 Last modified: 2024-03-11

Priority: 3

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

22.10.4 [func.require]/4 says:

> […] This forwarding step delivers a state entity of type `T` as cv `T&` when the call is performed on an lvalue of the call wrapper type and as cv `T&&` otherwise, where cv represents the cv-qualifiers of the call wrapper and where cv shall be neither `volatile` nor `const volatile`.

The "shall" seemingly indicates a requirement for user codes that calling a volatile perfect forwarding call wrapper is undefined behavior. Such requirement seems to be combined with requirements for implementations in an unusual way.

Moreover, it is unclear whether UB is intended here. Perhaps we only want calling such a `volatile` wrapper to be conditionally-supported. E.g. when the `operator()` happens to be implementable as a static member function, calling a `volatile` wrapper can be naturally supported and have expected behavior.

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll.

LWG 2487(i) doesn't seem to intend to forbid `volatile` support as a conforming extension.

P1065R2 made it ill-formed for `std::bind` but didn't make the same change to the definition of perfect forwarding call wrapper. So given an `as_volatile` function that works like `std::as_const`, `as_volatile(std::bind(&T::fn))(t)` is ill-formed but `as_volatile(std::mem_fn(&T::fn))(t)` is undefined. Supporting it as a conforming extension is fine, but making it UB is not. Maybe "conditionally supported" instead?

Proposed resolution:

This wording is relative to N4964.

1. Modify 22.10.4 [func.require] as indicated:

   > 4 A perfect forwarding call wrapper is an argument forwarding call wrapper that forwards its state entities to the underlying call expression. This forwarding step delivers a state entity of type `T` as cv `T&` when the call is performed on an lvalue of the call wrapper type and as cv `T&&` otherwise, where cv represents the cv-qualifiers of the call wrapper and where cv shall be neither `volatile` nor `const volatile`, except that if cv is either `volatile` or `const volatile`, it is unspecified whether the call is well-formed.

### 4008(i). §[range.utility.conv.to] `ranges::to` may cause infinite recursion if `range_value_t<C>` is a non-move-constructible range

Section: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: S. B. Tam Opened: 2023-11-08 Last modified: 2024-03-11

Priority: 3

View other active issues in [range.utility.conv.to].

View all other issues in [range.utility.conv.to].

View all issues with New status.

Discussion:

25.5.7.2 [range.utility.conv.to]/2 says:

> 1. (2.1) — If `C` does not satisfy `input_range` or `convertible_to<range_reference_t<R>, range_value_t<C>>` is `true`:

>    1. […]

> 2. (2.2) — Otherwise, if `input_range<range_reference_t<R>>` is `true`:

>    >     to<C>(r | views::transform([](auto&& elem) {
>    >       return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
>    >     }), std::forward<Args>(args)...);

> 3. […]

That is, if `range_reference_t<R>` is not convertible to `range_value_t<C>`, and `range_reference_t<R>` is an input range, `views::transform` is applied to convert the inner range to `range_value_t<C>` (through `to<range_value_t<C>>`), and then the transformed range is converted to `C` (through `to<C>`).

Consider:

>     #include <ranges>

>     struct ImmovableRange {
>       ImmovableRange(int*, int*);
>       ImmovableRange(ImmovableRange&&) = delete;

>       int* begin();
>       int* end();
>     };

>     struct C {
>       ImmovableRange* begin();
>       ImmovableRange* end();
>     };

>     using R = int[1][2];

>     void test() {
>       (void)std::ranges::to<C>(R{});
>     }

Here:

1. `convertible_to<range_reference_t<R>, range_value_t<C>>` is `false`.

2. `range_reference_t<R>` satisfies `input_range`.

3. `range_reference_t<R>` can be converted to `range_value_t<C>` through `to<range_value_t<C>>`. (If it couldn't, an error would be produced immediately.)

So `to<C>` is called recursively, constructing `C` with the transformed range (whose `range_reference_t<R>` is the same as `range_value_t<C>`). For the construction from the transformed range:

1. `range_reference_t<R>` and `range_value_t<C>` are both `ImmovableRange`.

2. `convertible_to<range_reference_t<R>, range_value_t<C>>` (i.e. `convertible_to<ImmovableRange, ImmovableRange>`) is `false`.

3. `range_reference_t<R>` (i.e. `ImmovableRange`) satisfies `input_range`.

4. `range_reference_t<R>` can be converted to `range_value_t<C>` through `to<range_value_t<C>>`.

So `to<C>` is called recursively again, transforming the range for the second time. This time, the transformation does not change any of the above four facts. As a result, `to<C>` is called yet again, leading to an infinite recursion.

I believe this can be fixed by stop calling `to<C>` recursively when `range_reference_t<R>` is the same as `range_value_t<C>`.

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll.

"Do we want `same_as` or `!different-from`?"

Proposed resolution:

This wording is relative to N4964.

1. Modify 25.5.7.2 [range.utility.conv.to] as indicated:

   >     template<class C, input_range R, class... Args> requires (!view<C>)
   >       constexpr C to(R&& r, Args&&... args);

   > > -1- Mandates: `C` is a cv-unqualified class type.

   > > -2- Returns: An object of type `C` constructed from the elements of `r` in the following manner:

   > > 1. (2.1) — If `C` does not satisfy `input_range` or `convertible_to<range_reference_t<R>, range_value_t<C>>` is `true`:

   > >    1. […]

   > > 2. (2.2) — Otherwise, if `same_as<range_reference_t<R>, range_value_t<C>>` is `false` and `input_range<range_reference_t<R>>` is `true`:

   > >    >     to<C>(r | views::transform([](auto&& elem) {
   > >    >       return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
   > >    >     }), std::forward<Args>(args)...);

   > > 3. (2.3) — Otherwise, the program is ill-formed.

### 4010(i). `subrange::advance` should be improved

Section: 25.5.4.3 [range.subrange.access] Status: New Submitter: Hewill Kang Opened: 2023-11-09 Last modified: 2024-03-11

Priority: 3

View all other issues in [range.subrange.access].

View all issues with New status.

Discussion:

`subrange::advance` always increments `begin_` via `ranges::advance(begin_, n, end_)`, which has 𝒪(`n`) complexity for non-common random-access ranges, which can be improved to 𝒪(1) with the help of the `size_` member (if provided).

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll.

Jonathan: The "Effects: Equivalent to" wording strongly suggests doing exactly what it suggests there, and the difference would be observable in the number of times the iterator is compared to the sentinel. I'm not sure if we care about that, or if an implementation would be free to make this change as QoI. Regarding the proposed resolution, we know the type of `n` so we don't need to use `decltype(n)` in the cast.

Proposed resolution:

This wording is relative to N4964.

1. Modify 25.5.4.3 [range.subrange.access] as indicated:

       constexpr subrange& advance(iter_difference_t<I> n);

   > -9- Effects: Equivalent to:

   > >     if constexpr (bidirectional_iterator<I>) {
   > >       if (n < 0) {
   > >         ranges::advance(begin_, n);
   > >         if constexpr (StoreSize)
   > >           size_ += to-unsigned-like(-n);
   > >         return *this;
   > >       }
   > >     }

   > >     auto d = n - ranges::advance(begin_, n, end_);
   > >     if constexpr (StoreSize) {
   > >       n = std::min(n, static_cast<decltype(n)>(size_));
   > >       ranges::advance(begin_, n);
   > >       size_ -= to-unsigned-like(nd);
   > >     } else {
   > >       ranges::advance(begin_, n, end_);
   > >     }
   > >     return *this;

### 4017(i). Behavior of `std::views::split` on an empty range

Section: 25.7.17.3 [range.split.iterator], 25.7.16.3 [range.lazy.split.outer] Status: New Submitter: David Stone Opened: 2023-11-19 Last modified: 2024-06-24

Priority: 3

View all issues with New status.

Discussion:

Consider the following example (which uses `fmt::println` instead of `std::println`, but they do the same thing in C++23):

>     #include <iostream>
>     #include <string>
>     #include <ranges>
>     #include <fmt/ranges.h>

>     int main()
>     {
>       fmt::println("{}", std::views::split(std::string(" x "), ' '));
>       fmt::println("{}", std::views::split(std::string(" "), ' '));
>       fmt::println("{}", std::views::split(std::string("x"), ' '));
>       fmt::println("{}", std::views::split(std::string(""), ' '));
>     }

The output of this program (as specified today) is

>     [[], ['x'], []]
>     [[], []]
>     [['x']]
>     []

The principle set out in LWG 3478(i) is that splitting a sequence containing `N` delimiters should lead to `N+1` subranges. That principle was broken if the `N`-th delimiter was at the end of the sequence, which was fixed by P2210.

However, the principle is still broken if the sequence contains zero delimiters. A non-empty sequence will split into one range, but an empty sequence will split into zero ranges. That last line is incorrect — splitting an empty range on a delimiter should yield a range of an empty range — not simply an empty range.

Proposed Resolution: Currently, `split_view::iterator`'s constructor unconditionally initializes `trailing_empty_` to `false`. Instead, change 25.7.17.3 [range.split.iterator]/1 to initialize `trailing_empty_` to `cur_ == next_.begin()` (i.e. `trailing_empty_` is typically `false`, but if we're empty on initialization then we have to have a trailing empty range).

The following demo shows Barry Revzin's implementation from P2210, adjusted to fix this: godbolt.org/z/axWb64j9f

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll.

[2024-03; Reflector comments]

"For `split`, we need to adjust the definition of `end()` for the `common_range` case (which may require introducing a new constructor to the iterator); right now it would compare `ranges::end(base_)` against a value-initialized iterator, which is not in the domain of `==`. For `lazy_split`, we need to also change the non-forward overload."

"What should splitting an empty range on an empty pattern produce? Right now the behavior is that splitting a range of N > 0 elements with an empty pattern produces a range of N single-element ranges. I suppose you can argue that an empty pattern matches between adjacent elements but not at the start or end, so that an empty range, like a single-element range, contains 0 delimiters so should produce a range of one empty range. But it's also at least arguable that this should produce an empty range instead, so that we maintain the N element <-> N subrange and 1 element per subrange invariant.

Proposed resolution:

This wording is relative to N4964.

1. Modify 25.7.17.3 [range.split.iterator] as indicated:

   >     constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);

   > > -1- Effects: Initializes `parent_` with `addressof(parent)`, `cur_` with `std::move(current)`, and `next_` with `std::move(next)`, and `trailing_empty_` with `cur_ == next_.begin()`.

2. Modify 25.7.16.3 [range.lazy.split.outer] as indicated:

   >     constexpr outer-iterator(Parent& parent, iterator_t<Base> current)
   >       requires forward_range<Base>;

   > > -3- Effects: Initializes `parent_` with `addressof(parent)`, and `current_` with `std::move(current)`, and `trailing_empty_` with `current_ == ranges::end(parent.base_)`.

### 4018(i). `ranges::to`'s copy branch is underconstrained

Section: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2023-11-25 Last modified: 2024-03-11

Priority: 3

View other active issues in [range.utility.conv.to].

View all other issues in [range.utility.conv.to].

View all issues with New status.

Discussion:

Unlike other branches that return a prvalue `C`, this branch's `C` is returned by elidable move, indicating that `C` needs to be move constructible (demo):

>     #include <ranges>
>     #include <vector>

>     struct nonmovable {
>       nonmovable() = default;
>       nonmovable(const nonmovable&) = delete;
>       nonmovable& operator=(const nonmovable&) = delete;
>     };

>     template<class T>
>     struct nonmovable_vector : std::vector<T>, nonmovable { };

>     int main() {
>       int arr[] = {42};
>       auto v = std::ranges::to<nonmovable_vector<int>>(arr); // hard error
>     }

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4964.

1. Modify 25.5.7.2 [range.utility.conv.to] as indicated:

   >     template<class C, input_range R, class... Args> requires (!view<C>)
   >       constexpr C to(R&& r, Args&&... args);

   > > -1- Mandates: `C` is a cv-unqualified class type.

   > > -2- Returns: An object of type `C` constructed from the elements of `r` in the following manner:

   > > 1. (2.1) — If `C` does not satisfy `input_range` or `convertible_to<range_reference_t<R>, range_value_t<C>>` is `true`:

   > >    1. (2.1.1) — […]

   > >    2. (2.1.2) — […]

   > >    3. (2.1.3) — […]

   > >    4. (2.1.4) — Otherwise, if

   > >       1. (2.1.4.?) — `move_constructible<C>` is `true`,

   > >       2. (2.1.4.1) — `constructible_from<C, Args...>` is `true`, and

   > >       3. (2.1.4.2) — `container-insertable<C, range_reference_t<R>>` is `true`:

   > >          >     C c(std::forward<Args>(args)...);
   > >          >     if constexpr (sized_range<R> && reservable-container<C>)
   > >          >       c.reserve(static_cast<range_size_t<C>>(ranges::size(r)));
   > >          >     ranges::copy(r, container-inserter<range_reference_t<R>>(c));

   > >    5. (2.1.5) — Otherwise, the program is ill-formed.

   > > 2. (2.2) — […]

   > > 3. (2.3) — Otherwise, the program is ill-formed.

### 4019(i). Reversing an infinite range leads to an infinite loop

Section: 25.7.21 [range.reverse] Status: SG9 Submitter: Barry Revzin Opened: 2023-11-25 Last modified: 2024-11-14

Priority: 3

View all other issues in [range.reverse].

View all issues with SG9 status.

Discussion:

Consider the following:

>     auto a = views::iota(0) | views::reverse;
>     auto b = views::repeat(42) | views::reverse;

Here, `views::iota(0)` and `views::repeat(42)` are both non-common bidirectional (even random-access) ranges. They are also infinite ranges, even if the standard doesn't really recognize that.

`views::reverse` on a non-common range will actually compute the end iterator for you. So while both declarations of `a` and `b` above compile, attempting to use either in any way will lead to an infinite loop when you `try a.begin()` or `b.begin()`.

A reddit post suggested disallowing reversing a non-common range but that likely breaks reasonable use-cases. We could at the very least recognize ranges whose sentinel is `unreachable_t` and reject those from consideration. For instance, we could change 24.4.4.4 [range.iter.op.next]/3 to Mandate that `S` is not `unreachable_t`.

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll. Ask SG9 to look. Probably needs a paper.

Infinite ranges are invalid and giving an invalid range to the library is undefined. But this is not a particularly satisfactory answer given that we provide such ranges ourselves...

[St. Louis 2024-06-28; LWG and SG9 joint session]

Poll: SG9 and LWG believe this is not a defect?

    |SF| F| N| A|SA|
    | 3| 3| 0| 2| 0|

Weak consensus => needs to go to LEWG

Poll: SG9 and LWG agree that standard could do something to create fewer sharp edges here, and we encourage a paper exploring options.

    |SF| F| N| A|SA|
    | 4| 2| 2| 0| 0|

[2024-11-14; Related to LWG 4097(i).]

Proposed resolution:

### 4021(i). `mdspan::is_always_meow()` should be `noexcept`

Section: 23.7.3.6.1 [mdspan.mdspan.overview] Status: New Submitter: Stephan T. Lavavej Opened: 2023-12-07 Last modified: 2025-10-21

Priority: 3

View other active issues in [mdspan.mdspan.overview].

View all other issues in [mdspan.mdspan.overview].

View all issues with New status.

Discussion:

It can easily be proven that `mdspan::is_always_meow()` is preconditionless and can never throw exceptions. microsoft/STL has already shipped a `noexcept`-strengthened implementation, libc++ would like to (see llvm/llvm-project#74254), and together we believe that the Standard should simply mandate `noexcept` so users can rely on this. The proof is:

- N4964 23.7.3.6.1 [mdspan.mdspan.overview]/3:

  > `LayoutPolicy` shall meet the layout mapping policy requirements (23.7.3.4.3 [mdspan.layout.policy.reqmts]) […]

- 23.7.3.4.3 [mdspan.layout.policy.reqmts]/1:

  > A type `MP` meets the layout mapping policy requirements if for a type `E` that is a specialization of `extents`, `MP::mapping<E>` is valid and denotes a type `X` that meets the layout mapping requirements (23.7.3.4.2 [mdspan.layout.reqmts]) […]

- 23.7.3.4.2 [mdspan.layout.reqmts]/1, /1.5, /22, /24, /26:

  > A type `M` meets the layout mapping requirements if

  > […]

  > 1. — the following types and expressions are well-formed and have the specified semantics.

  >    […]

  >    `M::is_always_unique()`

  >    Result: A constant expression (7.7 [expr.const]) of type `bool`.

  >    […]

  >    `M::is_always_exhaustive()`

  >    Result: A constant expression (7.7 [expr.const]) of type `bool`.

  >    […]

  >    `M::is_always_strided()`

  >    Result: A constant expression (7.7 [expr.const]) of type `bool`.

Constant expressions can surely be obtained without throwing exceptions.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Use `if consteval` may lead to exception thrown from invocation of `is_always_meow`, when evaluated at runtime.

Design intent was for `is_always_meow` functions to always produce value at compile time. Layout requirements may need to be updated.

Separately, 16.3.2.4 [structure.specifications] says that Result: is just the type and value category; not "constant expression" - this matters because we need to require a prvalue constant expression here.

Proposed resolution:

This wording is relative to N4964.

1. Modify 23.7.3.6.1 [mdspan.mdspan.overview], class template `mdspan` synopsis, as indicated:

   >     […]
   >     static constexpr bool is_always_unique() noexcept
   >       { return mapping_type::is_always_unique(); }
   >     static constexpr bool is_always_exhaustive() noexcept
   >       { return mapping_type::is_always_exhaustive(); }
   >     static constexpr bool is_always_strided() noexcept
   >       { return mapping_type::is_always_strided(); }
   >     […]

### 4022(i). Ambiguity in the formatting of negative years with format specifier `%C`

Section: 30.12 [time.format] Status: Ready Submitter: Jiang An Opened: 2023-12-08 Last modified: 2026-06-15

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with Ready status.

Discussion:

Table 101, [tab:time.format.spec] reads:

Table 101 — Meaning of conversion specifiers [tab:time.format.spec]
Specifier | Replacement
--- | ---
`[…]`
`%C` | The year divided by `100` using floored division. If the result is a single decimal digit, it is prefixed with `0`. The modified command `%EC` produces the locale's alternative representation of the century.
`[…]`

When the year is in range `[-99, -10]`, it is unclear whether the result (in range `[-9, -1]`) is considered as a single decimal digit, and there is implementation divergence (llvm/llvm-project#74727).

Daniel:

This issue is somewhat related to LWG 3831(i).

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

Howard intended Option B, i.e. `"-09"`, but Option B could possibly be interpreted to mean `"0-9"`. "Maybe something like the 0 is inserted before the sole digit, or after the sign, if any."

Previous resolution [SUPERSEDED]

This wording is relative to N4964.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A:

1. Modify 30.12 [time.format], Table 101 [tab:time.format.spec], as indicated:

   > Table 101 — Meaning of conversion specifiers [tab:time.format.spec]
   > Specifier | Replacement
   > --- | ---
   > `[…]`
   > `%C` | The year divided by `100` using floored division. If the result is a single decimal digitin range `[0, 9]`, it is prefixed with `0`. The modified command `%EC` produces the locale's alternative representation of the century.
   > `[…]`

Option B:

1. Modify 30.12 [time.format], Table 101 [tab:time.format.spec], as indicated:

   > Table 101 — Meaning of conversion specifiers [tab:time.format.spec]
   > Specifier | Replacement
   > --- | ---
   > `[…]`
   > `%C` | The year divided by `100` using floored division. If the result is a single decimal digitin range `[-9, 9]`, it is prefixed with `0`. The modified command `%EC` produces the locale's alternative representation of the century.
   > `[…]`

[2025-10-17; Jonathan provides new wording based on Option B]

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5014.

1. Modify 30.12 [time.format], Table 133 [tab:time.format.spec], as indicated:

   > Table 133 — Meaning of conversion specifiers [tab:time.format.spec]
   > Specifier | Replacement
   > --- | ---
   > `[…]`
   > `%C` | The year divided by `100` using floored division. If the result is a single decimal digit, it is prefixed with `0` in the range `[-9, 9]` then `0` is inserted before the single digit (after the sign, if present). The modified command `%EC` produces the locale's alternative representation of the century.
   > `[…]`

### 4028(i). `std::is_(nothrow_)convertible` should be reworded to avoid dependence on the `return` statement

Section: 21.3.7 [meta.rel] Status: New Submitter: Jiang An Opened: 2023-12-18 Last modified: 2025-10-17

Priority: 2

View other active issues in [meta.rel].

View all other issues in [meta.rel].

View all issues with New status.

Discussion:

The current specification for `std::is_convertible` is sensitive to the requirements for the `return` statements. As a result, the requirements were accidentally changed by P0135R1 and then changed back by CWG issue 2426. The current revision of P2748 also plans to change the wording for `std::is_convertible` to avoid actual behavioral changing.

IMO it's better to specify `std::is_convertible` in a such way that is independent to `return` statements. The proposed resolution matches what mainstream implementations do, and should resolve LWG 3400(i) together.

[2025-10-17; Reflector poll.]

Set priority to 2 after reflector poll.

"I think the P/R is either insufficient or redundant: either remove the words "To is neither array nor function type and" (because they are unnecessary assuming the P/R's definition of `conv-dest` must be well-formed) or else add "To is neither cv void nor array nor function type" (because the P/R's definition of `conv-dest` remains ill-formed in cases like `is_convertible_v<int, void>`."

"Might need to revisit the hack added to 8.8.4 [stmt.return] by P2748R5."

Proposed resolution:

This wording is relative to N4971.

1. Modify 21.3.7 [meta.rel] as indicated:

   > Table 49 — Type relationship predicates [tab:meta.rel]
   > Template | Condition | Comments
   > --- | --- | ---
   > […] | […] | […]
   >     template<class From, class To>
   >     struct is_convertible;
   > | see below | `From` and `To` shall be complete types, `cv void`, or arrays of unknown bound.
   >     template<class From, class To>
   >     struct is_nothrow_convertible;
   > | `is_convertible_v<From, To>` is `true` and the conversion, as defined by `is_convertible`, is known not to throw any exceptions either both `From` and `To` are `cv void`, or the function call expression used for specifying `is_convertible` is non-throwing (7.6.2.7 [expr.unary.noexcept]) | `From` and `To` shall be complete types, `cv void`, or arrays of unknown bound.
   > […] | […] | […]

   > -5- The predicate condition for a template specialization `is_convertible<From, To>` shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

   > >     To test() {
   > >       return declval<From>();
   > >     }

   > 1. (?.1) — either both `From` and `To` are `cv void`, or

   > 2. (?.2) — `To` is neither array nor function type and the function call expression `conv-dest(declval<From>())` would be well-formed when treated as an unevaluated operand, where `conv-dest` is a hypothetical function declared as

   >    >     void conv-dest(To) noexcept;

   >    .

   > [Note 2: This requirement gives well-defined results for reference types, array types, function types, and cv `void`. — end note]

   > Access checking is performed in a context unrelated to `To` and `From`. Only the validity of the immediate context of the expression of the return statement (8.8.4 [stmt.return]) function call expression (7.6.1.3 [expr.call]) (including initialization of the returnedparameter object or reference) is considered.

### 4029(i). `basic_string` accidentally fails to meet the reversible container requirements

Section: 27.4.3.1 [basic.string.general] Status: New Submitter: Jan Schultke Opened: 2023-12-18 Last modified: 2024-03-15

Priority: 3

View all other issues in [basic.string.general].

View all issues with New status.

Discussion:

The complexity requirements for a reversible container (23.2.2.3 [container.rev.reqmts]) are that each function must have constant complexity. The corresponding member functions in 27.4.3.4 [string.iterators] have no complexity requirements, and `basic_string` unintentionally is not a reversible container (unless the implementation coincidentally provides constant complexity member functions).

[2024-03-15; Reflector poll]

Set priority to 3 after reflector poll.

Could harmonize with `vector` by saying: "A basic_string meets all of the requirements of a contiguous container (23.2.2.2 [container.reqmts]) and of a reversible container (23.2.2.3 [container.rev.reqmts])." It's not a sequence container (see LWG 718(i)) but as per 23.2.2.5 [container.alloc.reqmts] p1 it is allocator-aware (although it doesn't use `construct` and `destroy`).

Not comfortable removing the detailed descriptions for those members. We don't have the "Descriptions are only provided here for operations on [...] that are not described in one of these tables" wording that the containers have.

Proposed resolution:

This wording is relative to N4971.

> [Drafting Note: The proposed wording is similar to the specification in 23.3.13.1 [vector.overview] p2 and suggests to simply strike 27.4.3.4 [string.iterators] because it doesn't say anything new compared to 23.2.2.2 [container.reqmts] and 23.2.2.3 [container.rev.reqmts].

> Alternatively, one could add a

> Complexity: Constant.

> paragraph to each function in 27.4.3.4 [string.iterators], but that would be less clearer and would not explicitly say `basic_string` meets the reversible container requirements. ]

1. Modify 27.4.3.1 [basic.string.general] as indicated:

   > -2- A specialization of `basic_string` is a contiguous container (23.2.2.2 [container.reqmts]) and a reversible container (23.2.2.3 [container.rev.reqmts]).

   > -3- In all cases, `[data(), data() + size()]` is a valid range, `data() + size()` points at an object with value `charT()` (a "null terminator"), and `size() <= capacity()` is `true`.

   > >     namespace std {
   > >       template<class charT, class traits = char_traits<charT>,
   > >                class Allocator = allocator<charT>>
   > >       class basic_string {
   > >         […]
   > >         // 27.4.3.4 [string.iterators], iterators
   > >         constexpr iterator begin() noexcept;
   > >         constexpr const_iterator begin() const noexcept;
   > >         constexpr iterator end() noexcept;
   > >         constexpr const_iterator end() const noexcept;

   > >         constexpr reverse_iterator rbegin() noexcept;
   > >         constexpr const_reverse_iterator rbegin() const noexcept;
   > >         constexpr reverse_iterator rend() noexcept;
   > >         constexpr const_reverse_iterator rend() const noexcept;

   > >         constexpr const_iterator cbegin() const noexcept;
   > >         constexpr const_iterator cend() const noexcept;
   > >         constexpr const_reverse_iterator crbegin() const noexcept;
   > >         constexpr const_reverse_iterator crend() const noexcept;

   > >         // 27.4.3.5 [string.capacity], capacity
   > >         […]
   > >       };
   > >       […]
   > >     }

   > […]

2. Remove subclause 27.4.3.4 [string.iterators] in its entirety:

   > 23.4.3.4 Iterator support [string.iterators]

   >     constexpr iterator begin() noexcept;
   >     constexpr const_iterator begin() const noexcept;
   >     constexpr const_iterator cbegin() const noexcept;

   > > -1- Returns: An iterator referring to the first character in the string.

   > […]

   >     constexpr reverse_iterator rend() noexcept;
   >     constexpr const_reverse_iterator rend() const noexcept;
   >     constexpr const_reverse_iterator crend() const noexcept;

   > > -4- Returns: An iterator which is semantically equivalent to `reverse_iterator(begin())`.

### 4032(i). Possibly invalid types in the constraints of constructors of `std::shared_ptr`

Section: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: Jiang An Opened: 2023-12-25 Last modified: 2024-03-15

Priority: 4

View other active issues in [util.smartptr.shared.const].

View all other issues in [util.smartptr.shared.const].

View all issues with New status.

Discussion:

Currently, 20.3.2.2.2 [util.smartptr.shared.const]/3 and /9.1 says `Y(*)[N]` and `Y(*)[]`, however, they may be invalid types when `Y` is an array type of unknown bound or a function type. Presumably, the constraints should be satisfied only when the mentioned `Y(*)[N]` or `Y(*)[]` is valid.

[2024-03-15; Reflector poll]

Set priority to 4 after reflector poll.

Jens pointed out that "convertible", as a core language concept, goes from "expression" to "type", not from "type" to "type".

Previous resolution [SUPERSEDED]

This wording is relative to N4971.

1. Modify 20.3.2.2.2 [util.smartptr.shared.const] as indicated:

   >     template<class Y> explicit shared_ptr(Y* p);

   > > -3- Constraints: When `T` is an array type, the expression `delete[] p` is well-formed and either `T` is `U[N]` and `Y(*)[N]` is a valid type and convertible to `T*`, or `T` is `U[]` and `Y(*)[]` is a valid type and convertible to `T*`. When `T` is not an array type, the expression `delete p` is well-formed and `Y*` is convertible to `T*`.

   > > […]

   >     template<class Y, class D> shared_ptr(Y* p, D d);
   >     template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
   >     template<class D> shared_ptr(nullptr_t p, D d);
   >     template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

   > > -9- Constraints: `is_move_constructible_v<D>` is `true`, and `d(p)` is a well-formed expression. For the first two overloads:

   > > 1. (9.1) — If `T` is an array type, then either `T` is `U[N]` and `Y(*)[N]` is a valid type and convertible to `T*`, or `T` is `U[]` and `Y(*)[]` is a valid type and convertible to `T*`.

   > > 2. (9.2) — If `T` is not an array type, then `Y*` is convertible to `T*`.

   > > […]

[2024-03-15; Jonathan provides alternative wording]

Can we just use `is_convertible_v<Y(*)[N], T*>`? With `enable_if`-style SFINAE an invalid type will cause substitution failure and with a requires-clause the constraints won't be satisfied. Either way we get the desired outcome. Also, the delete expression is already required to be well-formed, which rules out function types, so that part of the issue is NAD.

Proposed resolution:

This wording is relative to N4971.

1. Modify 20.3.2.2.2 [util.smartptr.shared.const] as indicated:

   >     template<class Y> explicit shared_ptr(Y* p);

   > > -3- Constraints: When `T` is an array type, the expression `delete[] p` is well-formed and either: `T` is `U[N]` and `Y(*)[N]` is convertible to `T*`, or `T` is `U[]` and `Y(*)[]` is convertible to `T*`.

   > > 1. — `is_bounded_array_v<T> && is_convertible_v<Y(*)[rank_v<T>], T*>` is `true`, or
   > > 2. — `is_unbounded_array_v<T> && is_convertible_v<Y(*)[], T*>` is `true`.

   > > When `T` is not an array type, the expression `delete p` is well-formed and `Y*` is convertible to `T*` `is_convertible_v<Y*, T*>` is `true`.

   > > […]

   >     template<class Y, class D> shared_ptr(Y* p, D d);
   >     template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
   >     template<class D> shared_ptr(nullptr_t p, D d);
   >     template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

   > > -9- Constraints: `is_move_constructible_v<D>` is `true`, and `d(p)` is a well-formed expression. For the first two overloads:

   > > 1. (9.1) — If `T` is an array type, then either: `T` is `U[N]` and `Y(*)[N]` is convertible to `T*`, or `T` is `U[]` and `Y(*)[]` is convertible to `T*`.

   > >    1. — `is_bounded_array_v<T> && is_convertible_v<Y(*)[rank_v<T>], T*>` is `true`, or
   > >    2. — `is_unbounded_array_v<T> && is_convertible_v<Y(*)[], T*>` is `true`.

   > > 2. (9.2) — If `T` is not an array type, then `Y*` is convertible to `T*` `is_convertible_v<Y*, T*>` is `true`.

   > > […]

### 4033(i). §[macro.names] defining macros after importing the standard library

Section: 16.4.5.3.3 [macro.names] Status: New Submitter: Alisdair Meredith Opened: 2023-12-27 Last modified: 2024-08-02

Priority: 3

View all other issues in [macro.names].

View all issues with New status.

Discussion:

There are several places where the standard talks about what happens when including a header, and only sometimes do we address the notion of importing a header unit, or importing a standard library module. One global concern is that 16.4.5.3.3 [macro.names] prohibits users defining macros with a name that matches a name from any standard library header, but only if a standard library header is included. We should maintain that same prohibition on importing a standard header unit, and importing a standard library module. In general, we probably want some front matter to address the include vs. import wording issue in one place, rather than trying to update every usage — if it is already there I could not find it. It is also likely that some uses will want to say specific things about import vs. include and those cases will be handed to audit for if we have blanket front-matter wording. I also question whether "shall not" is the right way to prohibit users defining macros in their own code. Must a "shall not" restriction be diagnosed? I think the "right" answer is to make the program ill-formed, no diagnostic required. That is a foul answer, but UB does not really fit as there is no actual behavior to be undefined, unless we mean the process of translating the program is undefined, in which case IFNDR is the better way to say that. If we expect implementations to diagnose users defining macros that match library names we should say so more clearly, but I am not aware of any current tool-chain that does so.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll. "Why should `import std` care if I `#define vector potato`?"

Proposed resolution:

This wording is relative to N4971.

1. Modify 16.4.5.3.3 [macro.names] as indicated:

   > -1- If a translation unit includes a standard header, imports a standard header unit, or imports a standard library module, and provides a name specified in the standard library as an argument to `#define` or `#undef`, the program is ill-formed, no diagnostic requiredA translation unit that includes a standard library header shall not `#define` or `#undef` names declared in any standard library header.

   > -2- A translation unit shall not `#define` or `#undef` names lexically identical to keywords, to the identifiers listed in Table [tab:lex.name.special], or to the attribute-tokens described in 9.13 [dcl.attr], except that the names `likely` and `unlikely` may be defined as function-like macros (15.7 [cpp.replace]).

### 4034(i). Clarify specification of `std::min` and `std::max`

Section: 26.8.9 [alg.min.max] Status: New Submitter: Jan Schultke Opened: 2023-12-29 Last modified: 2024-08-02

Priority: 4

View other active issues in [alg.min.max].

View all other issues in [alg.min.max].

View all issues with New status.

Discussion:

All standard libraries effectively use the same implementation for `std::min(a, b)`, namely something along the lines of

>     return __b < __a ? __b : __a;

- MSVC STL

- libstdc++

- libc++

However, the wording in 26.8.9 [alg.min.max] is not so clear:

> Returns: The smaller value. Returns the first argument when the arguments are equivalent.

This leaves a few questions open:

- How is the smaller value determined? Must it be through the less-than operator?

- For `std::max`, can the "larger" value be determined through the greater-than operator or must it be through less-than?

[2024-08-02; Reflector poll]

Set priority to 4 after reflector poll. Suggestions for NAD. "The result can be determined in any way that is allowed by the constaints the function imposes on its arguments. Since the arguments must be Cpp17LessThanComparable, obviously it has to use a less-than operation, because it can't assume any other operation is possible." Some of the submitter's questions could be addressed with an editorial change.

Proposed resolution:

This wording is relative to N4971.

1. Modify 26.8.9 [alg.min.max] as indicated:

   >     template<class T>
   >       constexpr const T& min(const T& a, const T& b);

   > > -?- Preconditions: `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -?- Returns: `b < a ? b : a`.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Compare>
   >       constexpr const T& min(const T& a, const T& b, Compare comp);

   > > -?- Returns: `comp(b, a) ? b : a`.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >     constexpr const T& ranges::min(const T& a, const T& b, Comp comp = {}, Proj proj = {});

   > > -1- Preconditions: For the first form, `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -2- Returns: `comp(proj(b), proj(a)) ? b : a` The smaller value. Returns the first argument when the arguments are equivalent.

   > > -3- Complexity: Exactly one comparison and two applications of the projection, if any.

   > > -4- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr T min(initializer_list<T> r);

   > > -?- Preconditions: `ranges::distance(r) > 0`. `T` meets the Cpp17CopyConstructible (Table 32) and Cpp17LessThanComparable (Table 29) requirements.

   > > -?- Returns: The leftmost element `x` in `r` where `y < x` is `false` for all subsequent elements `y`.

   > > -?- Complexity: Exactly `ranges::distance(r) - 1` comparisons.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Compare>
   >       constexpr T min(initializer_list<T> r, Compare comp);

   > > -?- Preconditions: `ranges::distance(r) > 0`. `T` meets the Cpp17CopyConstructible requirements (Table 32).

   > > -?- Returns: The leftmost element `x` in `r` where `comp(y, x)` is `false` for all subsequent elements `y`.

   > > -?- Complexity: Exactly `ranges::distance(r) - 1` comparisons.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<copyable T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr T ranges::min(initializer_list<T> r, Comp comp = {}, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   >       requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
   >       constexpr range_value_t<R>
   >         ranges::min(R&& r, Comp comp = {}, Proj proj = {});

   > > -5- Preconditions: `ranges::distance(r) > 0`. For the overloads in namespace `std`, `T` meets the Cpp17CopyConstructible requirements. For the first form, `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -6- Returns: The leftmost element `x` in `r` where `comp(proj(y), proj(x))` is `false` for all subsequent elements `y` The smallest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the smallest.

   > > -7- Complexity: Exactly `ranges::distance(r) - 1` comparisons and twice as many applications of the projection, if any.

   > > -8- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr const T& max(const T& a, const T& b);

   > > -?- Preconditions: `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -?- Returns: `a < b ? b : a`.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Compare>
   >       constexpr const T& max(const T& a, const T& b, Compare comp);

   > > -?- Returns: `comp(a, b) ? b : a`.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {});

   > > -9- Preconditions: For the first form, `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -10- Returns: `comp(proj(a), proj(b)) ? b : a` The larger value. Returns the first argument when the arguments are equivalent.

   > > -11- Complexity: Exactly one comparison and two applications of the projection, if any.

   > > -12- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr T max(initializer_list<T> r);

   > > -?- Preconditions: `ranges::distance(r) > 0`. `T` meets the Cpp17CopyConstructible (Table 32) and Cpp17LessThanComparable (Table 29) requirements.

   > > -?- Returns: The leftmost element `x` in `r` where `x < y` is `false` for all subsequent elements `y`.

   > > -?- Complexity: Exactly `ranges::distance(r) - 1` comparisons.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<class T, class Compare>
   >       constexpr T max(initializer_list<T> r, Compare comp);

   > > -?- Preconditions: `ranges::distance(r) > 0`. `T` meets the Cpp17CopyConstructible requirements (Table 32).

   > > -?- Returns: The leftmost element `x` in `r` where `comp(x, y)` is `false` for all subsequent elements `y`.

   > > -?- Complexity: Exactly `ranges::distance(r) - 1` comparisons.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `T`.

   >     template<copyable T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr T ranges::max(initializer_list<T> r, Comp comp = {}, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   >       requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
   >       constexpr range_value_t<R>
   >         ranges::max(R&& r, Comp comp = {}, Proj proj = {});

   > > -13- Preconditions: `ranges::distance(r) > 0`. For the overloads in namespace `std`, `T` meets the Cpp17CopyConstructible requirements. For the first form, `T` meets the Cpp17LessThanComparable requirements (Table 29).

   > > -14- Returns: The leftmost element `x` in `r` where `comp(proj(x), proj(y)` is `false` for all subsequent elements `y` The largest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the largest.

   > > -15- Complexity: Exactly `ranges::distance(r) - 1` comparisons and twice as many applications of the projection, if any.

   > > -16- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

### 4039(i). §[ostream.formatted.print]: Inappropriate usage of `badbit` in definition of `vprint_unicode`/`vprint_nonunicode`

Section: 31.7.6.3.5 [ostream.formatted.print] Status: Ready Submitter: Jan Kokemüller Opened: 2024-01-13 Last modified: 2026-06-15

Priority: 4

View all other issues in [ostream.formatted.print].

View all issues with Ready status.

Discussion:

In section 31.7.6.3.5 [ostream.formatted.print], bullet 3.2 there is a mention of `badbit`:

> any exception thrown by the call to `vformat` is propagated without regard to the value of `os.exceptions()` and without turning on `ios_base::badbit` in the error state of `os`.

Now the affected functions `vprint_unicode` and `vprint_nonunicode` are specified to behave as formatted output functions (31.7.6.3.1 [ostream.formatted.reqmts]), which distinguishes two phases:

1. Output generation, which would call `setstate(ios_base::failbit)` in case of a failure (which may throw an exception)

2. The actual output itself, which would call `setstate(ios_base::badbit)` in case of an exception

The `vformat` call is obviously part of the first phase (since it generates the output sequence and not yet the actual output), and any failure here (such as an exception) would under regular formatted output function rules set `ios_base::failbit` and not `ios_base::badbit` (the latter would indicate a loss of the actual output sequence integrity and is therefore set by the second phase in case of an exception).

Therefore mentioning `ios_base::badbit` in (3.2) doesn't really make sense, it could only meaningfully refer to `ios_base::failbit` instead.

[2025-10-16; Reflector poll]

Set priority to 4 after reflector poll.

"NAD. The reason it talks about badbit is that exceptions set badbit in IOStreams but not failbit. If we don’t want to hook into the stream’s error handling mentioning badbit is correct."

Previous resolution [SUPERSEDED]

This wording is relative to N4971.

1. Modify 31.7.6.3.5 [ostream.formatted.print] as indicated:

   >     void vprint_unicode(ostream& os, string_view fmt, format_args args);
   >     void vprint_nonunicode(ostream& os, string_view fmt, format_args args);

   > > -3- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of `os`, except that:

   > > 1. (3.1) — failure to generate output is reported as specified below, and

   > > 2. (3.2) — any exception thrown by the call to `vformat` is propagated without regard to the value of `os.exceptions()` and without turning on `ios_base::failbitbadbit` in the error state of `os`.

   > > […]

[2025-10-16; Daniel provides new wording]

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5014.

1. Modify 31.7.6.3.5 [ostream.formatted.print] as indicated:

   >     void vprint_unicode(ostream& os, string_view fmt, format_args args);
   >     void vprint_nonunicode(ostream& os, string_view fmt, format_args args);

   > > -3- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of `os`, except that:

   > > 1. (3.1) — failure to generate output is reported as specified below, and

   > > 2. (3.2) — any exception thrown by the call to `vformat` is propagated without regard to the value of `os.exceptions()` and without turning on `ios_base::failbit` or `ios_base::badbit` in the error state of `os`.

   > > […]

### 4040(i). Contradictory specification of `std::tuple_size`

Section: 22.4.7 [tuple.helper] Status: New Submitter: Jiang An Opened: 2024-01-20 Last modified: 2024-03-11

Priority: 3

View all other issues in [tuple.helper].

View all issues with New status.

Discussion:

22.4.7 [tuple.helper]/1 currently says:

> All specializations of `tuple_size` meet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic of `integral_constant<size_t, N>` for some `N`.

which is probably wrong for `std::tuple_size<int>` and `std::tuple_size<std::vector<int>>` whose definitions are not provided, and contradicts with 22.4.7 [tuple.helper]/4

> […] Otherwise, it has no member `value`.

Also, there's currently implementation divergence on whether `std::tuple_size<cv T>` is a complete type when `std::tuple_size<T>::value` is absent (related to LWG 2770(i)). Some implementations (e.g. libc++ and libstdc++) treat 22.4.7 [tuple.helper]/4 and D.15 [depr.tuple]/2 as "`std::tuple_size<cv T>` is generated from partial specializations only if `std::tuple_size<T>` meets the aforementioned conditions", while others (e.g. MSVC STL) always consider `std::tuple_size<cv T>` needs to be defined. The latter reading seems to be justified by 16.4.2.2 [contents]/1 and 22.4.2 [tuple.syn].

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll. The first part of the issue was fixed editorially.

Proposed resolution:

### 4041(i). The requirements on literal type in [concept.swappable] should be removed

Section: 18.4.9 [concept.swappable] Status: New Submitter: Jiang An Opened: 2024-01-20 Last modified: 2024-03-15

Priority: 4

View other active issues in [concept.swappable].

View all other issues in [concept.swappable].

View all issues with New status.

Discussion:

18.4.9 [concept.swappable] bullet (2.3.1) currently requires `T` to be a literal type in order to make the swapping expression a constant expression in that case. The requirement was likely automatically enforced by the core language rules in C++20 and thus essentially redundant.

However, as P2448R2 relaxed the restrictions on constexpr functions, it seems that the swapping expression can be a constant expression even if `T` is not a literal type.

E.g. the following program is accepted by GCC/libstdc++ in C++23 mode (demo).

>     #include <concepts>

>     struct NonLiteral {
>       NonLiteral() {} // non-constexpr
>       constexpr NonLiteral(const NonLiteral&) noexcept {};
>       constexpr NonLiteral& operator=(const NonLiteral&) noexcept { return *this; };
>     };

>     int main()
>     {
>       NonLiteral x;
>       static_assert((std::ranges::swap(x, x), true));
>     }

IMO there's no good reason to additionally require literal types since C++23, which would complicate implementations.

[2024-03-15; Reflector poll]

Set priority to 4 after reflector poll.

Concerned about 7.7 [expr.const]/5.16 (can only modify non-volatile lvalues of literal type in constant expressions). Unable see a non-contrived case where this issue matters.

N.B. `ranges::swap` needs the "reified object" treatment; the repetitions of `E1` and `E2` are not pure textual repetitions of the argument expressions.

Can we just eliminate all uses of "literal type"?

Wouldn't we still require a constexpr destructor?

Proposed resolution:

This wording is relative to N4971.

1. Modify 18.4.9 [concept.swappable] as indicated:

   > -2- The name `ranges::swap` denotes a customization point object (16.3.3.3.5 [customization.point.object]). The expression `ranges::swap(E1, E2)` for subexpressions `E1` and `E2` is expression-equivalent to an expression `S` determined as follows:

   > 1. (2.1) — […]

   > 2. (2.2) — […]

   > 3. (2.3) — Otherwise, if `E1` and `E2` are lvalues of the same type `T` that models `move_constructible<T>` and `assignable_from<T&, T>`, `S` is an expression that exchanges the denoted values. `S` is a constant expression if

   >    1. (2.3.1) — `T` is a literal type (6.9.1 [basic.types.general]),

   >    2. (2.3.2) — both `E1 = std::move(E2)` and `E2 = std::move(E1)` are constant subexpressions (3.15 [defns.const.subexpr]), and

   >    3. (2.3.3) — the full-expressions of the initializers in the declarations

   >       >     T t1(std::move(E1));
   >       >     T t2(std::move(E2));

   >       are constant subexpressions.

   >    `noexcept(S)` is equal to `is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>`.

   > 4. (2.4) — Otherwise, `ranges::swap(E1, E2)` is ill-formed.

   > […]

### 4046(i). Effects of inserting into or erasing from flat container adaptors when an exception is thrown need to be more permissive

Section: 23.2.7.2 [associative.reqmts.except], 23.6.8.1 [flat.map.overview], 23.6.9.1 [flat.multimap.overview], 23.6.11.1 [flat.set.overview], 23.6.12.1 [flat.multiset.overview] Status: New Submitter: Jiang An Opened: 2024-01-23 Last modified: 2024-03-15

Priority: 2

View all issues with New status.

Discussion:

The issue was originally discovered while implementing `flat_(multi_)set` in MSVC STL (microsoft/STL#4059).

Currently, the effects of inserting a single element into or erasing elements from a flat container adaptor when an exception is thrown, are implicitly specified in 23.2.7.2 [associative.reqmts.except].

However, given erasing elements from a `vector` or a `deque` generally needs to assign elements, which is not guaranteed to be non-throwing, it seems unreasonable to require `erase` of flat container adaptors not to throw in such a case. Likewise, as inserting a single value into `deque` is not guaranteed to be effectless when an exception is thrown, so flat container adaptors are unlike to provide such a guarantee.

Perhaps we should conditionally relax the requirements when the underlying containers can't provide them.

[2024-03-15; Reflector poll]

Set priority to 2 after reflector poll.

P2767 §3.5 is related/conflicting.

Proposed resolution:

### 4047(i). Explicitly specifying template arguments for `std::swap` should not be supported

Section: 16.4.5.2.1 [namespace.std] Status: New Submitter: Jiang An Opened: 2024-01-25 Last modified: 2024-03-15

Priority: 4

View other active issues in [namespace.std].

View all other issues in [namespace.std].

View all issues with New status.

Discussion:

`std::swap` overloads are separately declared in many standard library headers. It is likely expected that when a program includes correct headers for standard library types to be swapped, a `std::swap` call, with template argument deduced from arguments, selects the correct overload.

However, if one explicitly specifies template arguments for a `std::swap` call, there would be possibly undesired overloads for which templates arguments are fully obtained, and thus undesired instantiation and ill-formed may be triggered.

Perhaps we should explicitly state that explicitly specifying templates for `std::swap` is unsupported (having unspecified effects and possibly ill-formed), like taking address of non-address functions (16.4.5.2.1 [namespace.std]/6) and explicitly specifying template arguments for most standard algorithms (26.2 [algorithms.requirements]/15), to avoid possible reading that there are some guarantees, derived from signatures of overloads, for doing so.

[2024-03-15; Reflector poll]

Set priority to 4 after reflector poll.

NAD - if a user does this and it breaks, so be it.

NAD as an issue creating a new kind of library policy for the first time. Needs a paper.

Not unique to `std::swap`. We should forbid this throughout the standard library.

Closely related to LWG 2146(i).

Proposed resolution:

### 4048(i). Inconsistent preconditions for transparent insertion of `std::flat_map/std::flat_set`

Section: 23.6.11 [flat.set], 23.6.8.1 [flat.map.overview] Status: New Submitter: Jiang An Opened: 2024-01-26 Last modified: 2024-03-15

Priority: 2

View all issues with New status.

Discussion:

The preconditions for transparent insertion of associative containers (23.4.3.4 [map.modifiers]/13, 23.4.3.4 [map.modifiers]/29, and 23.4.6.4 [set.modifiers]/3) detect the results of `equal_range`, while those for `std::flat_set` and `std::flat_map` (23.6.11.5 [flat.set.modifiers]/2 and 23.6.8.7 [flat.map.modifiers]/20) currently detect the results of `find`, which is inconsistent.

During implementing `std::flat_set` in MSVC STL, it was reported (microsoft/STL#4105) that the current preconditions for `std::flat_set::insert` can lead to inconsistent results. Tim Song told that the current preconditions were copied from old revisions of P2363. So, presumably we should change these preconditions for flat container adaptors to consistently use `equal_range`.

[2024-03-15; Reflector poll]

Set priority to 2 after reflector poll.

We didn't want `equal_range(k) == equal_range(u)` because the two sides of the `==` need to be evaluated at different times.

It seems to me that `equal_range(k) == equal_range(u)` ought to be true before the insertion, and still true after the insertion. The wording in 23.4.6.4 [set.modifiers]/3 and 23.4.3.4 [map.modifiers]/13 requires only that the condition be true before the insertion. We either want to change the wording here to be consistent with those places; or else change 23.4.6.4 [set.modifiers] and 23.4.3.4 [map.modifiers] to be consistent with the saner wording here. I'd vote for the latter.

Proposed resolution:

This wording is relative to N4971.

1. Modify 23.6.8.7 [flat.map.modifiers] as indicated:

   >     template<class K, class... Args>
   >       pair<iterator, bool> try_emplace(K&& k, Args&&... args);
   >     template<class K, class... Args>
   >       iterator try_emplace(const_iterator hint, K&& k, Args&&... args);

   > > […]

   > > -20- Preconditions: The conversion from `k` into `key_type` constructs an object `u`, for which `find(k) == find(u)equal_range(k) == equal_range(u)` is `true`.

2. Modify 23.6.11.5 [flat.set.modifiers] as indicated:

   >     template<class K> pair<iterator, bool> insert(K&& x);
   >     template<class K> iterator insert(const_iterator hint, K&& x);

   > > […]

   > > -2- Preconditions: The conversion from `x` into `value_type` constructs an object `u`, for which `find(x) == find(u)equal_range(x) == equal_range(u)` is `true`.

### 4049(i). C `<foo.h>` headers not in freestanding

Section: 16.4.2 [organization] Status: New Submitter: Ben Craig Opened: 2024-01-26 Last modified: 2024-08-21

Priority: 3

View all issues with New status.

Discussion:

There are many C-ish `<cfoo>` headers in freestanding. There isn't anything that requires the matching C `<foo.h>` headers to be in freestanding.

[2024-08-21; Reflector poll]

Set priority to 3 after reflector poll. Some discomfort requiring `errno.h` and `wchar.h` to be freestanding in C++ when they are not in C23. `string.h` is not freestanding in C17, but is in C23 (but C++ is still based on C17). `stdlib.h` is "conditionally partially freestanding" in C23, but not in C17. Suggestion to incorporate this into P3348.

Proposed resolution:

This wording is relative to N4971.

1. Modify 16.4.2.3 [headers] as indicated:

   > -9- 17.15 [support.c.headers], C standard library headers, describes the effects of using the `name.h` (C header) form in a C++ program. The `name.h` form is the corresponding C header form.

2. Modify 16.4.2.5 [compliance] as indicated:

   > -2- A freestanding implementation has an implementation-defined set of headers. This set shall include at least the headers shown in Table 27 and the corresponding C header form (16.4.2.3 [headers]) of each of the C++ headers for C library facilities (Table 25) included in Table 27.

### 4051(i). A less hacky and more useful way to compare comparison category types

Section: 17.12.2 [cmp.categories] Status: New Submitter: Corentin Jabot Opened: 2024-01-31 Last modified: 2025-10-23

Priority: 3

View all other issues in [cmp.categories].

View all issues with New status.

Discussion:

Comparison categories can only be compared to the literal `0`. It does not make sense for them to be comparable to anything else, so conceptually, the design of P0515 makes sense, however in practice it's a pain point from users and implementations alike, as the desired semantics is barely implementable.

- One implementation strategy is to use `nullptr_t`. This produces warnings in implementations because using a `0` pointer constant is not recommended, and filtering out these warnings would be unreliable.

- Another implementation strategy is to require a `consteval` expression but

  - This permits `1-1`, which ought to be totally fine and yet it exposes users to UB

  - It is not SFINAE friendly (and attempts at SFINAE are presumably UB).

And there are use cases where SFINAE friendliness is important, notably testing frameworks.

The status quo has engendered multiple issues being reported to at least 3 vendors

Suggestion:

- Allow any `0` constant expression

- Require compile magic to SFINAE on non zero values

- Remove the gratuitous UB

The proposed wording requires compiler magic and has been implemented in clang. (Other possible way to implement that would for example be a magic type attribute, or a magic type)

Related vendor issues:

GCC Bugzilla issue 96278

GCC Bugzilla issue 100903

LLVM issue 43670

LLVM pulls request 79465

Microsoft STL issue 4359

Microsoft STL pull request 3581

Visual Studio issue 10509214

snitch issue 140

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

We should prefer rejecting the undefined expression at compile time, rather than leading to UB at runtime.

There is implementation divergence for SFINAE checks between the standard library implementations. The `has_a_value(std::strong_ordering::equal)` is `false` for libstdc++, and `true` for libc++, MSVC:

>     auto has_a_value(const auto& t) {
>       if constexpr (requires { t.has_value(); }) {
>         return t.has_value();
>       } else if (constexpr (requires { t != nullptr; }) {
>         return t != nullptr;
>       } else {
>         return true;
>       }
>     }

Proposed resolution:

This wording is relative to N4971.

1. Modify 17.12.2.1 [cmp.categories.pre] as indicated:

   > -3- The relational and equality operators for the comparison category types are specified with an anonymous parameter of unspecified type. This type shall be selected by the implementation such that these parameters can only accept an integral constant expression evaluating toliteral `0` as a corresponding argument.

   > [Example 1: `nullptr_t` meets this requirement. — end example]

   > In this context, the behavior of a program that supplies an argument other than a literal `0` is undefined.

### 4052(i). Bogus requirements for `piecewise_linear_distribution`

Section: 29.5.9.6.2 [rand.dist.samp.pconst], 29.5.9.6.3 [rand.dist.samp.plinear] Status: New Submitter: Jonathan Wakely Opened: 2024-02-05 Last modified: 2026-06-15

Priority: 4

View all other issues in [rand.dist.samp.pconst].

View all issues with New status.

Discussion:

In the second constructor of 29.5.9.6.3 [rand.dist.samp.plinear], P1719R2 replaced:

>     template<class InputIteratorB, class InputIteratorW>
>       piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
>                                     InputIteratorW firstW);

> Effects: [...] Moreover, the id-expressions `iterator_traits<InputIteratorB>::value_type` and `iterator_traits<InputIteratorW>::value_type` shall each denote a type that is convertible to `double`.

with

>     template<class InputIteratorB, class InputIteratorW>
>       piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
>                                     InputIteratorW firstW);

> Mandates: `is_invocable_r_v<double, UnaryOperation&, double>` is `true`.

> Preconditions: [...]

> Effects: [...]

This was a copy & paste error from the next constructor, and was obviously not intended. There is no `UnaryOperation` in that constructor.

A less obviously wrong issue is the use of `double` there in the first place. Shouldn't it be `RealType` instead? That seems to be incorrect throughout both 29.5.9.6.2 [rand.dist.samp.pconst] and 29.5.9.6.3 [rand.dist.samp.plinear], and was only partially fixed by LWG 1439(i).

Finally, the preconditions also say:

> Preconditions: [...] If `firstB == lastB` or `++firstB == lastB`, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, [`firstB`, `lastB`) forms a sequence b of length n + 1, the length of the sequence w starting from `firstW` is at least n, and any wk for k ≥ n are ignored by the distribution.

These are not preconditions. I think it is trying to say something like:

> Preconditions: [...] [`firstB`, `lastB`) is a valid range and [`firstW`, `firstW + (lastB - firstB)`) is a valid range.

> Effects: If `firstB == lastB` or `++firstB == lastB`, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, let [`firstB`, `lastB`) form a sequence b0, …, bn, and let wk = `*firstW++` for k = 0, …, n.

The equivalent constructor for `piecewise_constant_distribution` has similar problems with its preconditions in terms of n + 1.

[2024-03-12; Reflector poll]

Set priority to 4 after reflector poll. The copy & paste error was fixed editorially.

[2025-10-06; Hewill comments and provides wording]

We should fix the constructor, otherwise the following should be rejected according to the standard:

>     #include <random>

>     struct Op {
>       float operator()(float);
>       void operator()(auto) = delete;
>     };

>     static_assert(!std::is_invocable_r_v<double, Op&, double>);

>     int main() {
>       std::initializer_list<float> l;
>       std::piecewise_linear_distribution<float> dist(l, Op{});
>     }

This is, because it violates Mandates. However, all three compilers accept it because `Op&` can take `float` and return `float`.

Previous resolution [SUPERSEDED]

This wording is relative to N5014.

1. Modify 29.5.9.6.2 [rand.dist.samp.pconst] as indicated:

   >     template<class InputIteratorB, class InputIteratorW>
   >       piecewise_constant_distribution(InputIteratorB firstB, InputIteratorB lastB,
   >                                       InputIteratorW firstW);

   > > -4- Mandates: Both of

   > > 1. (4.1) — `is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>`

   > > 2. (4.2) — `is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>`

   > > are `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_constant_distribution(initializer_list<RealType> bl, UnaryOperation fw);

   > > -7- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_constant_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);

   > > -10- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

2. Modify 29.5.9.6.3 [rand.dist.samp.plinear] as indicated:

   >     template<class InputIteratorB, class InputIteratorW>
   >       piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
   >                                     InputIteratorW firstW);

   > > -4- Mandates: Both of

   > > 1. (4.1) — `is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>`

   > > 2. (4.2) — `is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>`

   > > are `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_linear_distribution(initializer_list<RealType> bl, UnaryOperation fw);

   > > -7- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_linear_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);

   > > -10- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

[2026-06-03; Jonathan provides new wording]

[Brno 2026-06-12; LWG review]

We need to restore the precondition that the iterators form valid ranges.

Proposed resolution:

This wording is relative to N5046.

1. Modify 29.5.9.6.2 [rand.dist.samp.pconst] as indicated:

   >     template<class InputIteratorB, class InputIteratorW>
   >       piecewise_constant_distribution(InputIteratorB firstB, InputIteratorB lastB,
   >                                       InputIteratorW firstW);

   > > -4- Mandates: Both of

   > > 1. (4.1) — `is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>`

   > > 2. (4.2) — `is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>`

   > > are `true`.

   > > -5- Preconditions: `InputIteratorB` and `InputIteratorW` each meet the Cpp17InputIterator requirements (24.3.5.3 [input.iterators]). If `firstB == lastB` or `++firstB == lastB`, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, [`firstB`, `lastB`) forms a sequence b of length n + 1, the length of the sequence w starting from `firstW` is at least n, and any wk for k ≥ n are ignored by the distribution.

   > > -6- Effects: Constructs a `piecewise_constant_distribution` object with parameters as specified above. calculated from the following values: If `distance(firstB, lastB)` is less than 2, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, let n = `distance(firstB, lastB) - 1`, the range [`firstB`, `lastB`) forms a sequence b and the counted range `firstW`+[0, n) forms a sequence w.

   >     template<class UnaryOperation>
   >       piecewise_constant_distribution(initializer_list<RealType> bl, UnaryOperation fw);

   > > -7- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_constant_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);

   > > -10- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

2. Modify 29.5.9.6.3 [rand.dist.samp.plinear] as indicated:

   >     template<class InputIteratorB, class InputIteratorW>
   >       piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB,
   >                                     InputIteratorW firstW);

   > > -4- Mandates: Both of

   > > 1. (4.1) — `is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>`

   > > 2. (4.2) — `is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>`

   > > are `true`.

   > > -5- Preconditions: `InputIteratorB` and `InputIteratorW` each meet the Cpp17InputIterator requirements (24.3.5.3 [input.iterators]). If `firstB == lastB` or `++firstB == lastB`, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, [`firstB`, `lastB`) forms a sequence b of length n + 1, the length of the sequence w starting from `firstW` is at least n + 1, and any wk for k ≥ n + 1 are ignored by the distribution.

   > > -6- Effects: Constructs a `piecewise_linear_distribution` object with parameters as specified above. calculated from the following values: If `distance(firstB, lastB)` is less than 2, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, let n = `distance(firstB, lastB) - 1`, the range [`firstB`, `lastB`) forms a sequence b and the counted range `firstW`+[0, n + 1) forms a sequence w.

   >     template<class UnaryOperation>
   >       piecewise_linear_distribution(initializer_list<RealType> bl, UnaryOperation fw);

   > > -7- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

   >     template<class UnaryOperation>
   >       piecewise_linear_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);

   > > -10- Mandates: `is_invocable_r_v<result_typedouble, UnaryOperation&, result_typedouble>` is `true`.

   > > […]

### 4055(i). §[iterator.operations] `std::distance` is missing a precondition

Section: 24.4.3 [iterator.operations] Status: Ready Submitter: Jan Schultke Opened: 2024-02-25 Last modified: 2026-06-15

Priority: 4

View other active issues in [iterator.operations].

View all other issues in [iterator.operations].

View all issues with Ready status.

Discussion:

`std::distance` for random access iterators is defined in terms of `(last - first)` (24.4.3 [iterator.operations] p5) for Cpp17RandomAccessIterators.

However, this subtraction is not guaranteed to be well-defined (24.3.5.7 [random.access.iterators], Table [tab:randomaccessiterator]):

> Table 91: Cpp17RandomAccessIterator requirements (in addition to Cpp17BidirectionalIterator) [tab:randomaccessiterator]
> Expression | Return type | Operational semantics | Assertion/note pre-/post-condition
> --- | --- | --- | ---
> `[…]`
> `b - a` | `difference_type` | `return n;` | Preconditions: there exists a value `n` of type `difference_type` such that `a + n == b`. `b == a + (b - a)`.

For example, pointer subtraction is undefined if the result isn't representable as `std::ptrdiff_t`, and user-defined types with random access iterators aren't required to have a difference which is always representable by `difference_type`.

`std::distance(&a, &b)` can't be well-defined when `&b - &a` is not, so `std::distance` is missing a precondition.

[2024-03-12; Reflector poll]

Set priority to 4 after reflector poll.

The proposed change is wrong, the new wording only associates with the second condition, but should also apply when "`last` is reachable from `first`".

Previous resolution [SUPERSEDED]

This wording is relative to N4971.

1. Modify 24.4.3 [iterator.operations] as indicated:

   >     template<class InputIterator>
   >       constexpr typename iterator_traits<InputIterator>::difference_type
   >         distance(InputIterator first, InputIterator last);

   > > -4-Preconditions: `last` is reachable from `first`, or `InputIterator` meets the Cpp17RandomAccessIterator requirements, and `first` is reachable from `last` , and `typename iterator_traits<InputIterator>::difference_type` can represent the result of this function call.

   > > -5- Effects: If `InputIterator` meets the Cpp17RandomAccessIterator requirements, returns `(last - first)`; otherwise, increments `first` until `last` is reached and returns the number of increments.

[2024-03-12; Jonathan provides improved wording]

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N4971.

1. Modify 24.4.3 [iterator.operations] as indicated:

   >     template<class InputIterator>
   >       constexpr typename iterator_traits<InputIterator>::difference_type
   >         distance(InputIterator first, InputIterator last);

   > > -4-Preconditions: `last` is reachable from `first`, or `InputIterator` meets the Cpp17RandomAccessIterator requirements and `first` is reachable from `last`. The return type can represent the result.

   > > -5- Effects: If `InputIterator` meets the Cpp17RandomAccessIterator requirements, returns `(last - first)`; otherwise, increments `first` until `last` is reached and returns the number of increments.

### 4057(i). `generator::iterator`'s `operator*` is not `noexcept` when it can be

Section: 25.8.6 [coro.generator.iterator] Status: New Submitter: S. B. Tam Opened: 2024-03-01 Last modified: 2024-09-17

Priority: 3

View other active issues in [coro.generator.iterator].

View all other issues in [coro.generator.iterator].

View all issues with New status.

Discussion:

`generator::iterator`'s `operator*` is specified to have the following noexcept-specifier:

>     noexcept(is_nothrow_copy_constructible_v<reference>)

When `reference` is an rvalue reference type, `is_nothrow_copy_constructible_v<reference>` is `false` (because `reference` is not copy constructible), which means `operator*` is not `noexcept`.

However, `operator*` doesn't perform any potentially-throwing operation in this case. It's effect is equivalent to `return static_cast<reference>(*p.value_);` (where the type of `p.value_` is effectively `add_pointer_t<reference>`). Neither the dereference nor the cast to rvalue reference can throw an exception.

I think the expression inside the noexcept-specifier should be changed to `noexcept(static_cast<reference>(*p.value_))`.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. "Just remove the noexcept-specifier." "Maybe add `is_reference_v<reference> || ...`"

[2024-09-16; Casey provides wording]

In the prioritization thread, LWG discussed a couple of alternatives without much enthusiasm. One of the alternatives was to simply remove the noexcept-specifier, which seems to be the most efficient use of LWG's time.

Proposed resolution:

This wording is relative to N4988.

1. Modify 25.8.6 [coro.generator.iterator] as indicated:

   >     namespace std {
   >       template<class Ref, class Val, class Allocator>
   >       class generator<Ref, Val, Allocator>::iterator {
   >         […]
   >         reference operator*() const noexcept(is_nothrow_copy_constructible_v<reference>);
   >         […]
   >       };
   >     }

   > […]

   > -3- Returns: `*this`.

   >     reference operator*() const noexcept(is_nothrow_copy_constructible_v<reference>l);

   > -4- Preconditions: For some generator object `x`, `coroutine_` is in `*x.active_` and `x.active_->top()` refers to a suspended coroutine with promise object `p`.

### 4058(i). `std::to_address()` should be SFINAE-friendly

Section: 20.2.4 [pointer.conversion] Status: LEWG Submitter: Peter Kasting Opened: 2024-03-13 Last modified: 2025-10-23

Priority: 3

View all other issues in [pointer.conversion].

View all issues with LEWG status.

Discussion:

LWG 3545(i) made `std::pointer_traits` SFINAE-friendly. However, `std::to_address` is still not required to be SFINAE-friendly.

This requires callers who wish to accept both pointer-like and non-pointer-like types to guard calls with a constraint that reimplements the core logic of `to_address`, such as the following:

    template<typename T>
    concept IsPointerLike = requires { typename std::pointer_traits<T>::pointer; }
                             || requires (const T& t) { t.operator->(); };

Making `to_address` not be SFINAE-friendly was seen as desirable, so `std::contiguous_iterator` would produce a hard error for types marked with `contiguous_iterator_tag` that do not properly support `to_address`. Thus any fix should not regress that unless LWG explicitly decides to do so. Also note that libc++'s current implementation of `to_address`, which is SFINAE-friendly, does not produce such a hard error.

[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]

There is support for both having `std::to_address` SFINAE friendly, and producing hard error early, for iterators that define `iterator_concept` as contiguous but fail to satisfy the concept due to not providing `operator->()`. LEWG should decide on which direction is prefered.

Proposed resolution:

This wording is relative to N4971.

1. Modify 20.2.4 [pointer.conversion] as indicated:

   > `template<class Ptr> constexpr auto to_address(const Ptr& p) noexcept;`

   > -?- Constraints: Either the expression `pointer_traits<Ptr>::to_address(p)` is well-formed (see 20.2.3.4 [pointer.traits.optmem]), or the expression `p.operator->()` is well-formed.

   > -3- Returns: `pointer_traits<Ptr>::to_address(p)` if that expression is well-formed (see 20.2.3.4 [pointer.traits.optmem]), otherwise `to_address(p.operator->())`.

2. Modify 24.3.4.14 [iterator.concept.contiguous] as indicated:

   > -1- The `contiguous_iterator` concept provides a guarantee that the denoted elements are stored contiguously in memory.

   >     template<class I>
   >       concept contiguous_iterator =
   >         random_access_iterator<I> &&
   >         derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
   >         is_lvalue_reference_v<iter_reference_t<I>> &&
   >         same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
   >         requires(const I& i) {
   >           { to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
   >         }
   >         std::same_as<decltype([] { return std::to_address(std::declval<I>()); }()),
   >                      std::add_pointer_t<std::iter_reference_t<I>>>;

   [The submitter welcomes less awkward alternatives to achieve the desired hard error for `contiguous_iterator`.]

### 4059(i). Leaky abstraction in `join_with_view`'s iterator

Section: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: S. B. Tam Opened: 2024-03-23 Last modified: 2024-08-11

Priority: 3

View other active issues in [range.join.with.iterator].

View all other issues in [range.join.with.iterator].

View all issues with New status.

Discussion:

`join_with_view`'s iterator is defined in terms of `variant`, `visit` and `get`, which implies that the iterator operations throw `bad_variant_access` if the underlying variant is valueless-by-exception (which can happen if an underlying iterator has a throwing copy constructor).

IMO, the `variant` is an implementation detail and shouldn't be exposed. It's confusing for users to get `bad_variant_access` when user code does not deal with `variant`.

The spec of `join_with_view` is also inconsistent with `concat_view` as recently added to the working draft by P2542R8. The latter has "`it_.valueless_by_exception()` is `false`" as a precondition.

I believe that `join_with_view` should similarly require that `inner_it_.valueless_by_exception()` is `false` for each iterator operation.

(FWIW, MSVC STL implements `join_with_view` with a trimmed-down version of `variant`, and having to throw `bad_variant_access` causes a small maintenance burden.)

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. We like the suggestion to require "not valueless" as a precondition.

[2024-08-08, Inbal Levi provides wording]

Proposed resolution:

This wording is relative to N4988.

1. Modify 25.7.15.3 [range.join.with.iterator] as indicated:

   >     constexpr iterator(iterator<!Const> i)
   >         requires Const && convertible_to<iterator_t<V>, OuterIter> &&
   >                  convertible_to<iterator_t<InnerRng>, InnerIter> &&
   >                  convertible_to<iterator_t<Pattern>, PatternIter>;

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -10- Effects: […]

   >     constexpr decltype(auto) operator*() const;

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -12- Effects: Equivalent to: […]

   >     constexpr iterator& operator++();

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -13- Effects: Equivalent to: […]

   >     constexpr void operator++(int);

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -14- Effects: Equivalent to: `++*this`.

   >     constexpr iterator operator++(int)
   >       requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>;

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -15- Effects: Equivalent to: […]

   >     constexpr iterator& operator--()
   >       requires ref-is-glvalue && bidirectional_range<Base> &&
   >                bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;

   > > -?- Preconditions: `inner_it_.valueless_by_exception()` is `false`.

   > > -16- Effects: Equivalent to: […]

### 4062(i). `ranges::empty` has no semantic requirements for `forward_range`s

Section: 25.3.13 [range.prim.empty] Status: New Submitter: Hewill Kang Opened: 2024-03-30 Last modified: 2025-10-20

Priority: 3

View all issues with New status.

Discussion:

This is a small part of issues raised by already-closed P3156.

Currently, `ranges::empty` (25.3.13 [range.prim.empty]) is always a valid expression when applied to `forward_range`s, because even if it does not have `.empty()` or `.size()` we can always check whether it is empty by comparing its `begin()` and `end()`, which is reflected in the standard preference to use it to check whether a certain `forward_range` is empty, such as in the Effects of `split_view::find-next()` (25.7.17.2 [range.split.view]) and `cartesian_product_view::end()` (25.7.33.2 [range.cartesian.view]).

In addition, MSVC-STL also uses `ranges::empty` in the implementation of `ranges::contains_subrange` for the check.

However, unlike `ranges::size`, which has a `sized_range` concept to ensure semantics, `ranges::empty` has no corresponding one. This makes it lack of time complexity guarantees and semantics for the meaning of the returned value when using the bullets (2.2) and (2.3) of 25.3.13 [range.prim.empty] to check emptiness.

Perhaps we need to add semantic requirements for `ranges::empty`, but this seems inconsistent with the current wording as no other CPOs have.

Alternatively, maybe we could move bullets targeting `forward_range`s to the very beginning so that `ranges::empty` always has correct semantics with `bool(ranges::begin(t) == ranges::end(t))` when applied to `forward_range`s.

[2025-10-20; Reflector poll.]

Set priority to 3 after reflector poll.

"This is the part of P3156 that is actually a defect. We need to require ranges::empty to actually mean what we think it means. Would be reasonable to add a sematic requirement to the `range` concept that if `ranges::empty(r)` is well-formed then it does what it should do (equal to `bool(begin == end)`, has amortized constant complexity, does not modify `r`, is not required to be valid after `begin` for non-forward)."

Proposed resolution:

### 4063(i). Freestanding `std::char_traits<char>::eof` depends on non-freestanding `EOF`

Section: 27.2.4.2 [char.traits.specializations.char] Status: New Submitter: Jiang An Opened: 2024-04-03 Last modified: 2024-05-08

Priority: 2

View all other issues in [char.traits.specializations.char].

View all issues with New status.

Discussion:

Currently `EOF` is not yet freestanding, because it's only specified to be provided in `<stdio.h>` in C, or together with `<cstdio>` in C++.

However, `std::char_traits<char>::eof` (required to be freestanding since C++26 by P2338R4) is required to return the value of `EOF`. It's unclear what is expected to be done in a freestanding implementation.

Related to LLVM issue #85158.

[2024-05-08; Reflector poll]

Set priority to 2 after reflector poll.

"The `eof`, `not_eof`, `to_int_type` and `to_char_type` members of `char_traits` are only needed by iostreams, so I see no reason for them to be freestanding. In libstdc++ `eof` and `not_eof` depend on hosted, so are not freestanding. Now that we can have partially freestanding classes in the library, that seems like the right solution here."

"`int_type`/`char_type`/`eof` is kind of poor-man (or C)'s `optional<char_type>`. Inclined to support it in freestanding on that basis, even if the only standard component that uses it is iostreams."

"No implementer is confused about the value of `eof()`/`EOF`. Having the value specified in the non-freestanding part of the standard should be good enough."

"Prefer to make EOF freestanding, but don't feel strongly about it. The important thing is keeping `string_view` working."

Proposed resolution:

### 4065(i). Requirements for fancy pointers might be insufficient for self-referential implementation of containers

Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2024-04-07 Last modified: 2024-05-01

Priority: 3

View other active issues in [allocator.requirements.general].

View all other issues in [allocator.requirements.general].

View all issues with New status.

Discussion:

Currently, 16.4.4.6.1 [allocator.requirements.general]/2.12 imposes a precondition for `pointer_traits::pointer_to` that `r` denotes an object whose storage is allocated by the allocator.

However, as mentioned in P0773R0, some containers are self-referential in current implementations, and thus a fancy pointer stored in the last node (as required by LWG 2261(i)) needs to be able to point to a sentinel node in the container object. Given the container object can be constructed at any location, perhaps we should specify that `pointer_traits::pointer_to` works on any lvalue.

[2024-05-01; Reflector poll]

Set priority to 3 after reflector poll.

"This applies to small-object optimizations too."

"An alternative way to support P0773's "near pointers" would be to require that a sentinel-node container using a "near allocator" must itself be constructed in the near segment. (This is P0773 (B) (2).)"

Proposed resolution:

This wording is relative to N4971.

1. Modify 16.4.4.6.1 [allocator.requirements.general] as indicated:

   > -2- In subclause 16.4.4.6 [allocator.requirements],

   > 1. […]

   > 2. (2.10) — `p` denotes a value of type `XX::pointer` obtained by calling `a1.allocate`, where `a1 == a`,

   > 3. (2.11) — […],

   > 4. (2.12) — `r` denotes an lvalue of type `T&` obtained by the expression `*p`,

   > 5. […]

   > […]

   >     pointer_traits<XX::pointer>::pointer_to(r)

   > > -33- Result: `XX::pointer`

   > > -34- Postconditions: Same as `p` Let `p1` denote the return value. `*p1` refers to the same object as `r`.

### 4066(i). `ranges::to` should reserve when `sized_sentinel_for` is satisfied

Section: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2024-04-13 Last modified: 2025-10-20

Priority: 4

View other active issues in [range.utility.conv.to].

View all other issues in [range.utility.conv.to].

View all issues with New status.

Discussion:

`ranges::to` currently only reserves when `r` satisfies `sized_range`. However, we can also extract its size when `r` is an `input_range` that its sentinel-iterator pair satisfies `sized_sentinel_for`.

Given that we have specifically designed the decision tree, I see no reason not to reserve in this case, since we'd be calling `ranges::begin(r)` anyway.

[2025-02-27; post-Hagenberg status]

The proposed resolution needs rebasing after P2846R6 was approved in Hagenberg. It also conflicts with LWG 3722(i).

[2025-10-20; Reflector poll.]

Set priority to 4 after reflector poll. Most votes were "P4 or NAD".

"I do not see a motivating example of `input_range` that would produce `sized_sentinel_for` for `begin`/`end` but would not be sized itself. However, the fix seems correct. Would prefer if would check if produced `subrange` is sized, instead of using a requires expression."

"Might as well do it, even though a `sized_sentinel_for`-but-not-sized input range feels like an extreme corner case."

"This is just applying the contrived case mentioned in LWG 3737(i) to `ranges::to`."

Previous resolution [SUPERSEDED]

This wording is relative to N4981.

1. Modify 25.5.7.2 [range.utility.conv.to] as indicated:

   >     template<class C, input_range R, class... Args> requires (!view<C>)
   >       constexpr C to(R&& r, Args&&... args);

   > > -1- Mandates: `C` is a cv-unqualified class type.

   > > -2- Returns: An object of type `C` constructed from the elements of `r` in the following manner:

   > > 1. (2.1) — […]

   > >    1. (2.1.1) — […]

   > >    2. (2.1.2) — […]

   > >    3. (2.1.3) — […]

   > >    4. (2.1.4) — Otherwise, if

   > >       1. (2.1.4.1) — `constructible_from<C, Args...>` is `true`, and

   > >       2. (2.1.4.2) — `container-appendable<C, range_reference_t<R>>` is `true`:

   > >          >     C c(std::forward<Args>(args)...);
   > >          >     subrange s{r};
   > >          >     if constexpr (sized_range<R>requires { s.size(); } && reservable-container<C>)
   > >          >       c.reserve(static_cast<range_size_t<C>>(ranges::size(r)s.size()));
   > >          >     ranges::for_each(rs, container-append(c));

[2025-10-20; Jonathan provides updated wording]

Proposed resolution:

This wording is relative to N5014.

1. Modify 25.5.7.2 [range.utility.conv.to] as indicated:

   >     template<class C, input_range R, class... Args> requires (!view<C>)
   >       constexpr C to(R&& r, Args&&... args);

   > > -1- Mandates: `C` is a cv-unqualified class type.

   > > -2- Returns: An object of type `C` constructed from the elements of `r` in the following manner:

   > > 1. (2.1) — […]

   > >    1. (2.1.1) — […]

   > >    2. (2.1.2) — […]

   > >    3. (2.1.3) — […]

   > >    4. (2.1.4) — Otherwise, if

   > >       1. (2.1.4.1) — `constructible_from<C, Args...>` is `true`, and

   > >       2. (2.1.4.2) — `container-appendable<C, range_reference_t<R>>` is `true`:

   > >          >     C c(std::forward<Args>(args)...);
   > >          >     subrange s{r};
   > >          >     if constexpr (sized_range<Rdecltype(s)> && reservable-container<C>)
   > >          >       c.reserve(static_cast<range_size_t<C>>(ranges::size(r)s.size()));
   > >          >     ranges::for_each(rs, container-append(c));

### 4067(i). Inconsistency and potential infinity meta-recursion in `std::chrono::zoned_time`'s constructors

Section: 30.11.7.2 [time.zone.zonedtime.ctor] Status: New Submitter: Jiang An Opened: 2024-04-14 Last modified: 2024-06-24

Priority: 3

View all other issues in [time.zone.zonedtime.ctor].

View all issues with New status.

Discussion:

Currently, there are no requirements on a program-defined `std::chrono::zoned_traits` specialization. So a `zoned_traits<UserDefinedTzPtr>::locate_zone` possibly returns a `string_view`, which leads to infinite meta-recursion when checking the constraints of `zoned_time`'s constructors that take a `string_view`.

Also, the constructor of `zoned_time` taking only one `string_view` is inconsistent with others and even self-inconsistent. For other constructors taking `string_view`, it is expected that the return value of `locate_zone` is only implicitly converted to a `TimeZonePtr`. But the return value is permitted to be convertible to `zoned_time` or `sys_time<Duration>` in this constructor. And given the `locate_name` function may be an overload set that distinguishes lvalues and rvalues of `string_view`, the use of `locate_time(string_view{})` in the constraints doesn't always reflect `locate_time(name)` in the effects as the return types can be different.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. "Such a `UserDefinedTzPtr` would be ridiculous and not useful, but it wouldn't hurt to tighten the spec a bit." "Would prefer a much simpler resolution saying 'dont do that'. I don't like that `locatable-as-tzptr` doesn't look like a dependent expression, but it is really. Saying '`TimeZonePtr` is implicit convertible from its return type' should be phrased in terms of converting to, and should talk about conversion from an expression to a type (see LWG 3105(i))."

Proposed resolution:

This wording is relative to N4971.

1. Modify 30.11.7.2 [time.zone.zonedtime.ctor] as indicated:

   > [Drafting note: Add the following two paragraphs to the very beginning of subclause 30.11.7.2 [time.zone.zonedtime.ctor]]

   > -?- Let `locatable-as-tzptr` be `true` if given an lvalue `sv` of type `string_view`, `traits::locate_zone(sv)` is well-formed and `TimeZonePtr` is implicitly convertible from its return type, and `false` otherwise. Only the validity of the immediate context of the invocation and conversion is considered.

   > -?- In every constructor that takes a `string_view` parameter `name`, let `converted-tzptr` be a variable of type `TimeZonePtr` that is copy-initialized from `traits::locate_zone(name)`.

   > […]

   >     explicit zoned_time(string_view name);

   > > -7- Constraints: `traits::locate_zone(string_view{})` is a well-formed expression and `zoned_time` is constructible from the return type of `traits::locate_zone(string_view{})` `locatable-as-tzptr` is `true`.

   > > -8- Effects: Initializes `zone_` with `traits::locate_zone(name)` and default constructs `tp_`.

   > […]

   >     zoned_time(string_view name, const sys_time<Duration>& st);

   > > -13- Constraints: `zoned_time` is constructible from the return type of `traits::locate_zone(name)` and `st` `locatable-as-tzptr` is `true`.

   > > -14- Effects: Equivalent to construction with `{traits::locate_zone(name)std::move(converted-tzptr), st}`.

   > […]

   >     zoned_time(string_view name, const local_time<Duration>& tp);

   > > -18- Constraints: `locatable-as-tzptr` is `true` and `zoned_time` is constructible from the return type of `traits::locate_zone(name)``TimeZonePtr` and `tp`.

   > > -19- Effects: Equivalent to construction with `{traits::locate_zone(name)std::move(converted-tzptr), tp}`.

   > […]

   >     zoned_time(string_view name, const local_time<Duration>& tp, choose c);

   > > -23- Constraints: `locatable-as-tzptr` is `true` and `zoned_time` is constructible from the return type of `traits::locate_zone(name)``TimeZonePtr`, `local_time<Duration>`, and `choose`.

   > > -24- Effects: Equivalent to construction with `{traits::locate_zone(name)std::move(converted-tzptr), tp, c}`.

   > […]

   >     template<class Duration2, class TimeZonePtr2>
   >       zoned_time(string_view name, const zoned_time<Duration2, TimeZonePtr2>& y);

   > > -32- Constraints: `locatable-as-tzptr` is `true` and `zoned_time` is constructible from the return type of `traits::locate_zone(name)``TimeZonePtr` and the type `zoned_time<Duration2, TimeZonePtr2>`.

   > > -33- Effects: Equivalent to construction with `{traits::locate_zone(name)std::move(converted-tzptr), y}`.

   >     template<class Duration2, class TimeZonePtr2>
   >       zoned_time(string_view name, const zoned_time<Duration2, TimeZonePtr2>& y, choose c);

   > > -34- Constraints: `locatable-as-tzptr` is `true` and `zoned_time` is constructible from the return type of `traits::locate_zone(name)``TimeZonePtr`, the type `zoned_time<Duration2, TimeZonePtr2>`, and the type `choose`.

   > > -35- Effects: Equivalent to construction with `{traits::locate_zone(name)std::move(converted-tzptr), y, c}`.

### 4068(i). Terminology for objects whose types model a concept

Section: 16.4.5.11 [res.on.requirements], 16.3.2.3 [structure.requirements] Status: New Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2025-10-14

Priority: 3

View other active issues in [res.on.requirements].

View all other issues in [res.on.requirements].

View all issues with New status.

Discussion:

During P2300 review the suggestion was raised that it might be helpful to be able to talk about "an object that models `Concept`" instead of "an object whose type models `Concept`" or other more verbose forms.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

"OK with finding a way to shorten this, but not as suggested above. Overloading the term "models Concept" for both objects and types would be confusing."

"Just say 'a `stoppable-source` object', or 'a `stoppable_token` object'"

Proposed resolution:

### 4070(i). Transcoding by `std::formatter<std::filesystem::path>`

Section: 31.12.6.9.2 [fs.path.fmtr.funcs] Status: Open Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2025-10-10

Priority: 2

View all issues with Open status.

Discussion:

31.12.6.9.2 [fs.path.fmtr.funcs] says:

> If `charT` is `char`, `path::value_type` is `wchar_t`, and the literal encoding is UTF-8, then the escaped path is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard [...]. Otherwise, transcoding is implementation-defined.

This seems to mean that the Unicode substitutions are only done for an escaped path, i.e. when the `?` option is used. Otherwise, the form of transcoding is completely implementation-defined. However, this makes no sense. An escaped string will have no ill-formed subsequences, because they will already have been replaced as per 28.5.6.5 [format.string.escaped]:

> Otherwise (X is a sequence of ill-formed code units), each code unit U is appended to E in order as the sequence `\x{hex-digit-sequence}`, where `hex-digit-sequence` is the shortest hexadecimal representation of U using lower-case hexadecimal digits.

So only unescaped strings can have ill-formed sequences by the time we do transcoding to `char`, but whether or not any u+fffd substitution occurs is just implementation-defined.

I believe we want to specify the substitutions are done when transcoding an unescaped path (and it doesn't matter whether we specify it for escaped paths, because it's a no-op if escaping happens first, as is apparently intended).

It does matter whether we escape first or perform substitutions first. If we escape first then every code unit in an ill-formed sequence is individually escaped as `\x{hex-digit-sequence}`. So an ill-formed sequence of two `wchar_t` values will be escaped as two `\x{...}` strings, which are then transcoded to UTF-8. If we transcode (with substitutions first) then the entire ill-formed sequence is replaced with a single replacement character, which will then be escaped as `\x{fffd}`. SG16 should be asked to confirm that escaping first is intended, so that an escaped string shows the original invalid code units. For a non-escaped string, we want the ill-formed sequence to be formatted as �, which the proposed resolution tries to ensure.

[2024-05-08; Reflector poll]

Set priority to 2 after reflector poll.

Previous resolution [SUPERSEDED]

This wording is relative to N4981.

1. Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:

   >     template<class FormatContext>
   >       typename FormatContext::iterator
   >         format(const filesystem::path& p, FormatContext& ctx) const;

   > > -5- Effects: Let `s` be `p.generic_string<filesystem::path::value_type>()` if the `g` option is used, otherwise `p.native()`. Writes `s` into `ctx.out()`, adjusted according to the path-format-spec. If `charT` is `char`, `path::value_type` is `wchar_t`, and the literal encoding is UTF-8, then the escaped path (possibly escaped) string is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. If `charT` and `path::value_type` are the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.

2. Modify the entry in the index of implementation-defined behavior as indicated:

   > transcoding of a formatted `path` when `charT` and `path::value_type` differ and not converting from `wchar_t` to UTF-8

[2025-06-11; SG16 comments and improves wording]

The "and not converting from `wchar_t` to UTF-8" wording added in the index of implementation-defined behavior by the current proposed resolution should be changed to "and the literal encoding is not UTF-8".

It was noted that "the literal encoding" is ambiguous in both the normative wording in 31.12.6.9.2 [fs.path.fmtr.funcs] p5 and in the new wording quoted above. In both cases, the intent is to refer to the "ordinary literal encoding". However, some SG16 participants were reluctant to include a drive-by fix with the proposed resolution for this issue since the ambiguous literal encoding reference i s a pre-existing and separable issue. Those same SG16 participants were more concerned that the same wording was used in both 31.12.6.9.2 [fs.path.fmtr.funcs] p5 and in the corresponding entry of the implementation-defined behavior index. I would defer to the LWG chair to decide whether to address this as an additional related clarification with this change or as a separate editorial or LWG issue.

The minimal change is to replace "and not converting from `wchar_t` to UTF-8" with "and the literal encoding is not UTF-8". The optional change is to insert "ordinary" before "literal encoding" as well. Once that is done, I'll have SG16 confirm they are content with the new proposed resolution.
Previous resolution [SUPERSEDED]

This wording is relative to N5008.

1. Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:

   >     template<class FormatContext>
   >       typename FormatContext::iterator
   >         format(const filesystem::path& p, FormatContext& ctx) const;

   > > -5- Effects: Let `s` be `p.generic_string<filesystem::path::value_type>()` if the `g` option is used, otherwise `p.native()`. Writes `s` into `ctx.out()`, adjusted according to the path-format-spec. If `charT` is `char`, `path::value_type` is `wchar_t`, and the ordinary literal encoding is UTF-8, then the escaped path (possibly escaped) string is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. If `charT` and `path::value_type` are the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.

2. Modify the entry in the index of implementation-defined behavior as indicated:

   > transcoding of a formatted `path` when `charT` and `path::value_type` differ and the ordinary literal encoding is not UTF-8

[2025-07-30; SG16 meeting]

SG16 unanimously approved new wording produced during the discussion. The group concluded that the intended behavior would be best specified by introducing additional names to denote the sequence of transformations that produce the intended effect. Status updated SG16 → Open.

Proposed resolution:

This wording is relative to N5014.

1. Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:

   >     template<class FormatContext>
   >       typename FormatContext::iterator
   >         format(const filesystem::path& p, FormatContext& ctx) const;

   > > -5- Effects: Let `s` be `p.generic_string<filesystem::path::value_type>()` if the `g` option is used, otherwise `p.native()`. Let `s2` be `s` adjusted according to the path-format-spec. Let `s3` be defined as follows:

   > > 1. (5.1) — If `charT` is `char`, `path::value_type` is `wchar_t`, and the ordinary literal encoding is UTF-8, `s3` is the result of transcoding `s2` from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with U+FFFD REPLACEMENT CHARACTER per the Unicode Standard, Chapter 3.9 U+FFFD Substitution in Conversion.
   > > 2. (5.2) — If `charT` and `path::value_type` are the same, then `s3` is the same as `s2`.
   > > 3. (5.3) — Otherwise, `s3` is the result of an implementation-defined transcoding of `s2`.
   > > Writes `s3` into `ctx.out()`. Writes `s` into `ctx.out()`, adjusted according to the path-format-spec. If `charT` is `char`, `path::value_type` is `wchar_t`, and the literal encoding is UTF-8, then the escaped path is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. If `charT` and `path::value_type` are the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.

2. Modify the entry in the index of implementation-defined behavior as indicated:

   > transcoding of a formatted `path` when `charT` and `path::value_type` differ and the ordinary literal encoding is not UTF-8

### 4073(i). `concat_view::size` may overflow

Section: 25.7.18.2 [range.concat.view] Status: New Submitter: Hewill Kang Opened: 2024-04-21 Last modified: 2024-06-24

Priority: 4

View other active issues in [range.concat.view].

View all other issues in [range.concat.view].

View all issues with New status.

Discussion:

Currently, `concat_view::size` returns the sum of sizes of all underlying ranges, and the return type is specified to be the common type of all sizes, which may lead to potential overflow:

>     auto r = std::views::iota(0uz, SIZE_MAX);
>     std::print("{}\n", r.size());            // 18446744073709551615 (size_t)
>     std::print("{}\n", r.end() - r.begin()); // 18446744073709551615 (__int128)

>     auto c = std::views::concat(r, r);
>     std::print("{}\n", c.size());            // 18446744073709551614 (size_t)
>     std::print("{}\n", c.end() - c.begin()); // 36893488147419103230 (__int128)

[2024-06-24; Reflector poll]

Set priority to 4 after reflector poll. Same behaviour as `join_view`, was there some discussion of this being covered by general wording?

Proposed resolution:

### 4077(i). Unclear preconditions of `std::ranges::generate_random`

Section: 26.12.2 [alg.rand.generate] Status: New Submitter: Jiang An Opened: 2024-04-21 Last modified: 2024-08-02

Priority: 2

View all other issues in [alg.rand.generate].

View all issues with New status.

Discussion:

Per discussion in cplusplus/draft#6904, the preconditions of `std::ranges::generate_random` are specified in an unusual way. It also appears that the "shall be equivalent" imposes requirements on both implementation and user code, and the requirements are not very clear. Perhaps we should clarify the conditions in usual Preconditions paragraphs.

[2024-08-02; Reflector poll]

Set priority to 2 after reflector poll. When reviewing this we were clear this imposes requirements on users.

Proposed resolution:

### 4078(i). What if arguments alias the output buffer in `std::format_to`?

Section: 28.5.5 [format.functions] Status: New Submitter: Jan Schultke Opened: 2024-04-24 Last modified: 2025-02-07

Priority: 4

View all other issues in [format.functions].

View all issues with New status.

Discussion:

>     int main() {
>       int x = 12345;
>       // note: this is "awoo" followed by 28 zeros (9.5.3 [dcl.init.string] p3)
>       char buffer[32] = "awoo";
>       std::format_to(buffer, "{}{}", x, buffer);
>       std::println("{}", buffer);
>     }

The output of this code is unspecified to be either `"1234512345"` or `"12345awoo"`, where GCC currently outputs the former. Formatting occurs through function calls (see 28.5.6.4 [format.formatter.spec] p1) and those cannot be unsequenced, however, it's also nowhere stated in what order the arguments get formatted and how the output iterator is advanced between calls to the formatters.

The status quo is undesirable because unspecified formatting output is not useful to the user, and the lack of a precondition which would forbid aliasing between arguments and the output buffer restricts the implementation.

My intuition is to add Preconditions specifications to functions in 28.5.5 [format.functions] which would forbid the output range of the output iterator to overlap ranges of the arguments.

A friend of mine has suggested to add a Preconditions to the BasicFormatter requirements instead, dealing with the issue at the root (`f.format(u, fc)`).

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll.

We should prohibit aliasing, so this is undefined.

Proposed resolution:

### 4080(i). Presumed value and difference types of an iterator type in ranges and non-ranges algorithms

Section: 24.3.2 [iterator.assoc.types] Status: New Submitter: Jiang An Opened: 2024-04-26 Last modified: 2024-06-24

Priority: 3

View all issues with New status.

Discussion:

Given an iterator type `I`, currently both `std::iter_value_t<I>` and `std::iterator_traits<I>::value_type` can be called the value type of `I`. These two types can be different if one specializes `std::indirect_readable_traits` for `I` such that `std::indirect_readable_traits<I>::value_type` is different from `I::value_type`.

It seems intended that ranges algorithms use `std::iter_value_t<I>` and non-ranges algorithms, along with other legacy iterator-related mechanisms, use `std::iterator_traits<I>::value_type`. But this doesn't seem clarified anywhere.

P2408R5 made the situation more complicated. In C++23, the type requirements of non-mutating iterators passed to many legacy algorithms were changed to C++20 iterator concepts, which are strongly associated with `std::iter_value_t`. It didn't seem intended that implementations were expected to use possibly different value types since C++23, but such reading might be suggested by the use of concepts.

Likewise, a non-random-access iterator type `I` can have two different difference types if one specializes `std::incrementable_traits` for `I` such that `std::incrementable_traits<I>::difference_type` is different from `I::difference_type`. It's also unclear which one should be used for non-mutating iterator in algorithms.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4081(i). `concat_view::iterator::operator-` is overconstrained

Section: 25.7.18.3 [range.concat.iterator] Status: New Submitter: Hewill Kang Opened: 2024-04-26 Last modified: 2024-08-02

Priority: 3

View other active issues in [range.concat.iterator].

View all other issues in [range.concat.iterator].

View all issues with New status.

Discussion:

Currently, two `concat_view::iterator`s can only be subtracted if `concat-is-random-access` is satisfied, which seems overconstrained since the implementation does not rely on all underlying ranges being `random_access_range` or `common_range`.

Generally speaking, iterators provide `operator-` mainly based on whether it satisfies `sized_sentinel_for` rather than being category-specific. For example, `counted_iterator`s that only model `input_iterator` can still be subtracted. We have no reason to reject the following:

>     std::list l = {1, 2, 3};
>     auto r = l | std::views::take(3);
>     auto c = std::ranges::concat_view{r};
>     auto it = c.begin();
>     it++;
>     auto d = it - c.begin(); // error: no match for 'operator-'

The proposed resolution lists a minimal constraint formula based on the implementation details of `operator-`.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.18.3 [range.concat.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range... Views>
   > >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   > >                   concatable<Views...>
   > >       template<bool Const>
   > >       class concat_view<Views...>::iterator {

   > >       public:
   > >         […]
   > >         friend constexpr difference_type operator-(const iterator& x, const iterator& y)
   > >           requires concat-is-random-access<Const, Views...>see below;
   > >         […]
   > >       };
   > >     }

   > […]

   >     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
   >       requires concat-is-random-access<Const, Views...>see below;

   > > -32- Preconditions: `x.it_.valueless_by_exception()` and `y.it_.valueless_by_exception()` are each `false`.

   > > -33- Effects: Let `ix` denote `x.it_.index()` and `iy` denote `y.it_.index()`.

   > > 1. (33.1) — if `ix > iy`, let `dy` be `ranges::distance(std::get<iy>(y.it_), ranges::end(std::get<iy>(y.parent_->views_)))`, `dx` be `ranges::distance(ranges::begin(std::get<ix>(x.parent_->views_)), std::get<ix>(x.it_))`. Let `s` denote the sum of the sizes of all the ranges `std::get<i>(x.parent_->views_)` for every integer `i` in the range `[iy + 1, ix)` if there is any, and `0` otherwise, of type `difference_type`, equivalent to:

   > >    >     return dy + s + dx;

   > >    (33.2) — otherwise, if `ix < iy` is `true`, equivalent to:

   > >    >     return -(y - x);

   > >    (33.3) — otherwise, equivalent to:

   > >    >     return std::get<ix>(x.it_) - std::get<iy>(y.it_);

   > > -?- Remarks: Let `Fs` be the pack that consists of all elements of `Views` except the last element, and let `Rs` be the pack that consists of all elements of `Views` except the first element. The expression in the requires-clause is equivalent to:

   > > >     (sized_sentinel_for<iterator_t<maybe-const<Const, Views>>,
   > > >                         iterator_t<maybe-const<Const, Views>>> && ...) &&
   > > >     (sized_sentinel_for<sentinel_t<maybe-const<Const, Fs>>,
   > > >                         iterator_t<maybe-const<Const, Fs>>> && ...) &&
   > > >     all-forward<Const, Rs...>

### 4087(i). Standard exception messages have unspecified encoding

Section: 17.9.3 [exception] Status: SG16 Submitter: Victor Zverovich Opened: 2024-04-28 Last modified: 2024-05-08

Priority: 3

View all other issues in [exception].

View all issues with SG16 status.

Discussion:

The null-terminated multibyte string returned by the `what` method of `std::exception` and its subclasses in the standard has an unspecified encoding. The closest thing in the specification is the "suitable for conversion and display as a `wstring`" part in Remarks (17.9.3 [exception] p6) but it is too vague to be useful because anything can be converted to `wstring` in one way or another:

>     virtual const char* what() const noexcept;

> > Returns: An implementation-defined ntbs.

> > Remarks: The message may be a null-terminated multibyte string (16.3.3.3.4.3 [multibyte.strings]), suitable for conversion and display as a `wstring` (27.4 [string.classes], 28.3.4.2.5 [locale.codecvt]). The return value remains valid until the exception object from which it is obtained is destroyed or a non-`const` member function of the exception object is called.

As a result, it is impossible to portably use the exception message, e.g. print it. Since exception messages are commonly combined with string literals and are often constructed from string literals, at the very least the standard should say that the message is compatible with them, i.e. that it is in the ordinary literal encoding or its subset.

To give a specific example of this problem, consider the following code compiled on Windows with Microsoft Visual C++, the ordinary literal encoding of UTF-8 and the system locale set to Belarusian (the language of the text in this example):

>     std::uintmax_t size = 0;
>     try {
>       size = std::filesystem::file_size(L"Шчучыншчына");
>     } catch (const std::exception& e) {
>       std::print("Памылка: {}", e.what());
>     }

Since both `std::filesystem::path` and `std::print` support Unicode one would expect this to work and, when run, print a readable error message if the file "Шчучыншчына" doesn't exist. However, the output will be corrupted instead. The reason for the corruption is that `filesystem_error` requires including the path in the message but doesn't say that it should be transcoded (31.12.7.2 [fs.filesystem.error.members] p7):

>     virtual const char* what() const noexcept;

> > Returns: An ntbs that incorporates the `what_arg` argument supplied to the constructor. The exact format is unspecified. Implementations should include the `system_error::what()` string and the pathnames of `path1` and `path2` in the native format in the returned string.

Therefore, the message will contain literal text in the ordinary literal encoding (UTF-8) combined with a path, most likely in the operating system dependent current encoding for pathnames which in this case is CP1251. So different parts of the output will be in two incompatible encodings and therefore unusable with `std::print` or any other facility.

The actual observable behavior for the above example is no output in the Windows console which is extremely broken but appears to be conformant with the current specification. It was reproduced with {fmt}'s implementation of `print` since Microsoft STL doesn't implement `std::print` yet. Replacing `std::print` with another output facility produces a different but equally unusable form of mojibake.

[2024-05-04; Daniel comments]

The proposed wording is incomplete. There are about 12 other `what` specifications in the Standard Library with exactly the same specification as `exception::what` that would either need to get the same treatment or we would need general wording somewhere that says that the specification "contract" of `exception::what` extends to all of its derived classes. A third choice could be that we introduce a new definition such as an lntbs (or maybe "literal ntbs") that is essentially an ntbs in the ordinary literal encoding.

[2024-05-08; Reflector poll]

Set priority to 3 after reflector poll. Send to SG16.

Proposed resolution:

This wording is relative to N4981.

1. Modify 17.9.3 [exception] as indicated:

   >     virtual const char* what() const noexcept;

   > > Returns: An implementation-defined ntbs in the ordinary literal encoding.

   > > Remarks: The message may be a null-terminated multibyte string (16.3.3.3.4.3 [multibyte.strings]), suitable for conversion and display as a `wstring` (27.4 [string.classes], 28.3.4.2.5 [locale.codecvt]). The return value remains valid until the exception object from which it is obtained is destroyed or a non-`const` member function of the exception object is called.

### 4089(i). `concat_view::iterator`'s `iter_swap` is overconstrained

Section: 25.7.18.3 [range.concat.iterator] Status: New Submitter: Hewill Kang Opened: 2024-04-30 Last modified: 2024-06-24

Priority: 3

View other active issues in [range.concat.iterator].

View all other issues in [range.concat.iterator].

View all issues with New status.

Discussion:

When the underlying iterator of two `concat_view::iterator`s are of different types, their `iter_swap` will dispatch `ranges::swap` to swap elements, which is reflected in its constraints of `swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>>`.

However, when the underlying iterators are all of the same type, `ranges::swap` will never be invoked, making checking for `swappable_with` unnecessary in this case.

This results in the current wording rejecting the following:

>     struct I {
>       using value_type = int;
>       using difference_type = int;
>       std::reference_wrapper<int> operator*() const;
>       I& operator++();
>       void operator++(int);
>       bool operator==(const I&) const;
>       friend void iter_swap(const I&, const I&); // custom iter_swap
>     };

>     static_assert(std::indirectly_swappable<I>);

>     int main() {
>       std::ranges::subrange<I, I> s1, s2;
>       std::ranges::swap_ranges(s1, s2);
>       std::ranges::concat_view c1{s1}, c2{s2};
>       std::ranges::swap_ranges(c1, c2); // ill-formed
>     }

Because `reference_wrapper` does not satisfy `swappable_with`, `concat_view::iterator` does not have a valid `iter_swap`, leading to the constraints of `swap_ranges` being unsatisfied.

This doesn't seem like it should be, and I think providing a simplified homogeneous `iter_swap` specialization for `concat_view::iterator` is reasonable.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. An extreme corner case, probably not worth caring about.

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.18.3 [range.concat.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range... Views>
   > >         requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
   > >                   concatable<Views...>
   > >       template<bool Const>
   > >       class concat_view<Views...>::iterator {

   > >       public:
   > >         using iterator_category = see below;                                // not always present.
   > >         using iterator_concept = see below;
   > >         using value_type = concat-value-t<maybe-const<Const, Views>...>;
   > >         using difference_type = common_type_t<range_difference_t<maybe-const<Const, Views>>...>;

   > >       private:
   > >         static constexpr bool concat-is-homogeneous = see below;                // exposition only
   > >         using base-iter =                                                       // exposition only
   > >           variant<iterator_t<maybe-const<Const, Views>>...>;

   > >         […]

   > >         friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   > >           requires concat-is-homogeneous &&
   > >                    indirectly_swappable<iterator_t<maybe-const<Const, Views...[0]>>>;
   > >         friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   > >           requires see below(!concat-is-homogeneous) &&
   > >                    swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> &&
   > >                    (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>);
   > >       }
   > >       […]
   > >     }

   > -?- `concat-is-homogeneous` is `true` if and only if the pack of types `iterator_t<maybe-const<Const, Views>>...` are all the same.

   > -1- `iterator::iterator_concept` is defined as follows:

   > […]

   >     friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   >       requires concat-is-homogeneous &&
   >                indirectly_swappable<iterator_t<maybe-const<Const, Views...[0]>>>;

   > > -?- Preconditions: `x.it_.valueless_by_exception()` and `y.it_.valueless_by_exception()` are each `false`.

   > > -?- Effects: Equivalent to:

   > > >     std::visit(ranges::iter_swap, x.it_, y.it_);

   > > -?- Remarks: The exception specification is equivalent to

   > > >     noexcept(ranges::iter_swap(std::get<0>(x.it_), std::get<0>(y.it_)))

   >     friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below)
   >       requires see below(!concat-is-homogeneous) &&
   >                swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> &&
   >                (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>);

   > > -42- Preconditions: `x.it_.valueless_by_exception()` and `y.it_.valueless_by_exception()` are each `false`.

   > > -43- Effects: Equivalent to:

   > > >     std::visit([&](const auto& it1, const auto& it2) {
   > > >         if constexpr (is_same_v<decltype(it1), decltype(it2)>) {
   > > >           ranges::iter_swap(it1, it2);
   > > >         } else {
   > > >           ranges::swap(*x, *y);
   > > >         }
   > > >       },
   > > >       x.it_, y.it_);

   > > -44- Remarks: The exception specification is equivalent to

   > > >     (noexcept(ranges::swap(*x, *y)) && ... && noexcept(ranges::iter_swap(its, its)))

   > > where `its` is a pack of lvalues of type `const iterator_t<maybe-const<Const, Views>>` respectively.

   > > The expression in the requires-clause is equivalent to

   > > >     swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> &&
   > > >     (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>)

### 4091(i). `concat_view` rejects non-movable references

Section: 25.7.18.2 [range.concat.view] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-06-24

Priority: 4

View other active issues in [range.concat.view].

View all other issues in [range.concat.view].

View all issues with New status.

Discussion:

In order to prevent non-equality-preserving behavior of `operator*` and `iter_move`, `concat_view` introduces the `concat-indirectly-readable` concept, part of which is:

>     template<class Ref, class RRef, class It>
>       concept concat-indirectly-readable-impl =                         // exposition only
>         requires (const It it) {
>           { *it } -> convertible_to<Ref>;
>           { ranges::iter_move(it) } -> convertible_to<RRef>;
>         };

This isn't quite right because `convertible_to` checks `is_convertible_v` which doesn't understand copy elision. This makes the current `concat_view` unable to work with ranges whose reference is non-movable prvalue:

>     auto r = std::views::iota(0, 5)
>            | std::views::transform([](int) { return NonMovable{}; });
>     auto c1 = std::ranges::concat_view(r);    // ill-formed, concat_indirectly_readable not satisfied
>     auto c2 = std::ranges::concat_view(r, r); // ditto

Since `std::visit<R>` is used in the implementation to perform reference conversion for the underlying iterator, the more accurate one should be `is_invocable_r` which does understand guaranteed elision.

Note that `join_with_view` has the same issue because `compatible-joinable-ranges` requires that the `value_type` of the inner range and pattern range must satisfy `common_with`, which always fails for non-movable types. However, this can be automatically resolved by LWG 4074(i)'s resolution.

[2024-06-24; Reflector poll]

Set priority to 4 after reflector poll. "Proposed resolution loses the existing requirement that the conversion is equality-preserving." "Don't care about rejecting non-movable reference types."

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.18.2 [range.concat.view] as indicated:

   > -1- The exposition-only `concat-indirectly-readable` concept is equivalent to:

   > >     template<class Ref, class CRef>
   > >     concept concat-ref-compatible-with = is_invocable_r_v<CRef, Ref()>;  // exposition only

   > >     template<class Ref, class RRef, class It>
   > >     concept concat-indirectly-readable-impl =                         // exposition only
   > >       requires (const It it) {
   > >         { *it } -> convertible_toconcat-ref-compatible-with<Ref>;
   > >         { ranges::iter_move(it) } -> convertible_toconcat-ref-compatible-with<RRef>;
   > >       };

   > >     template<class... Rs>
   > >       concept concat-indirectly-readable =                              // exposition only
   > >         common_reference_with<concat-reference-t<Rs...>&&,
   > >                               concat-value-t<Rs...>&> &&
   > >         common_reference_with<concat-reference-t<Rs...>&&,
   > >                               concat-rvalue-reference-t<Rs...>&&> &&
   > >         common_reference_with<concat-rvalue-reference-t<Rs...>&&,
   > >                               concat-value-t<Rs...> const&> &&
   > >         (concat-indirectly-readable-impl<concat-reference-t<Rs...>,
   > >                                          concat-rvalue-reference-t<Rs...>,
   > >                                          iterator_t<Rs>> && ...);

### 4092(i). The monotonic version of `common_iterator::operator==` is underconstrained

Section: 24.5.5.1 [common.iterator], 24.5.5.6 [common.iter.cmp] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-07-21

Priority: 3

View other active issues in [common.iterator].

View all other issues in [common.iterator].

View all issues with New status.

Discussion:

`common_iterator` has the following equality operator:

>     template<class I2, sentinel_for<I> S2>
>         requires sentinel_for<S, I2>
>       friend constexpr bool operator==(
>         const common_iterator& x, const common_iterator<I2, S2>& y);

which is quite useful when wrapping a C++20 `input_iterator` that does not model `equality_comparable` so that the quality operator required by the Cpp17InputIterator can still be synthesized.

However, the function signature does not check the correlation between `I2` and `I`, which allows a `common_iterator` wrapping two completely unrelated iterators to validly compare (demo):

>     common_iterator<string::iterator, unreachable_sentinel_t> i1;
>     common_iterator<list<int>::iterator, unreachable_sentinel_t> i2;
>     i1 == i2; // unfortunately compile

The proposed resolution requires `common_with<I, I2>` to be satisfied to enhance semantics, which is also consistent with the signature of `counted_iterator::operator==`.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. Seems unlikely to be a problem in practice. Proposed resolution would make the two `operator==` overloads ambiguous.

Previous resolution [SUPERSEDED]

This wording is relative to N4981.

1. Modify 24.5.5.1 [common.iterator] as indicated:

   >     namespace std {
   >       template<input_or_output_iterator I, sentinel_for<I> S>
   >         requires (!same_as<I, S> && copyable<I>)
   >       class common_iterator {
   >       public:
   >         […]
   >         template<classcommon_with<I> I2, sentinel_for<I> S2>
   >           requires sentinel_for<S, I2>
   >         friend constexpr bool operator==(
   >           const common_iterator& x, const common_iterator<I2, S2>& y);
   >         […]
   >       };
   >       […]
   >     }

2. Modify 24.5.5.6 [common.iter.cmp] as indicated:

   >     template<classcommon_with<I> I2, sentinel_for<I> S2>
   >       requires sentinel_for<S, I2>
   >     friend constexpr bool operator==(
   >       const common_iterator& x, const common_iterator<I2, S2>& y);

   > > -1- Preconditions: `x.v_.valueless_by_exception()` and `y.v_.valueless_by_exception()` are each `false`.

   > > […]

[2024-07-09; Hewill provides improved wording]

Proposed resolution:

This wording is relative to N4986.

1. Modify 24.5.5.1 [common.iterator] as indicated:

   >     namespace std {
   >       template<input_or_output_iterator I, sentinel_for<I> S>
   >         requires (!same_as<I, S> && copyable<I>)
   >       class common_iterator {
   >       public:
   >         […]
   >         template<classcommon_with<I> I2, sentinel_for<I> S2>
   >           requires sentinel_for<S, I2>
   >         friend constexpr bool operator==(
   >           const common_iterator& x, const common_iterator<I2, S2>& y);
   >         template<classcommon_with<I> I2, sentinel_for<I> S2>
   >           requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
   >         friend constexpr bool operator==(
   >           const common_iterator& x, const common_iterator<I2, S2>& y);
   >         […]
   >       };
   >       […]
   >     }

2. Modify 24.5.5.6 [common.iter.cmp] as indicated:

   >     template<classcommon_with<I> I2, sentinel_for<I> S2>
   >       requires sentinel_for<S, I2>
   >     friend constexpr bool operator==(
   >       const common_iterator& x, const common_iterator<I2, S2>& y);

   > > -1- Preconditions: `x.v_.valueless_by_exception()` and `y.v_.valueless_by_exception()` are each `false`.

   > > […]

   >     template<classcommon_with<I> I2, sentinel_for<I> S2>
   >       requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
   >     friend constexpr bool operator==(
   >       const common_iterator& x, const common_iterator<I2, S2>& y);

   > > -3- Preconditions: `x.v_.valueless_by_exception()` and `y.v_.valueless_by_exception()` are each `false`.

   > > […]

### 4093(i). `ranges::fold_left_first_with_iter` incorrectly constructs `optional<U>`

Section: 26.6.18 [alg.fold] Status: New Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24

Priority: 3

View other active issues in [alg.fold].

View all other issues in [alg.fold].

View all issues with New status.

Discussion:

`ranges::fold_left_first_with_iter` constructs the initial value through `optional<U>(in_place, *first)` which is not quite right, because the exposure constraint only ensures that `U` can be constructed from the value type of the iterator rather than its reference (demo):

>     #include <ranges>
>     #include <algorithm>

>     struct Op {
>       Op() = default;
>       Op(std::tuple<int>);
>       Op(std::tuple<int&>) = delete;
>       Op operator()(Op, std::tuple<int&>) const;
>     };

>     int main() {
>       std::ranges::fold_left_first_with_iter(
>         std::views::zip(std::views::single(0)),
>         Op{}
>       ); // hard error in libstdc++ and MSVC-STL
>     }

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. Unclear what "exposure constraint" means. "Don't want to require an extra copy to accommodate this corner case."

Proposed resolution:

This wording is relative to N4981.

1. Modify 26.6.18 [alg.fold] as indicated:

   >     template<input_iterator I, sentinel_for<I> S
   >              indirectly-binary-left-foldable<iter_value_t<I>, I> F>
   >       requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
   >       constexpr see below ranges::fold_left_first_with_iter(I first, S last, F f);
   >     template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F>
   >       requires constructible_from<range_value_t<R>, range_reference_t<R>>
   >       constexpr see below ranges::fold_left_first_with_iter(R&& r, F f);

   > > -9- Let `U` be

   > > >     decltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))

   > > -10- Effects: Equivalent to:

   > > >     if (first == last)
   > > >       return {std::move(first), optional<U>()};
   > > >     optional<U> init(in_place, iter_value_t<I>(*first));
   > > >     for (++first; first != last; ++first)
   > > >       *init = invoke(f, std::move(*init), *first);
   > > >     return {std::move(first), std::move(init)};

   > > -11- Remarks: The return type is `fold_left_first_with_iter_result<I, optional<U>>` for the first overload and `fold_left_first_with_iter_result<borrowed_iterator_t<R>, optional<U>>` for the second overload.

### 4094(i). `ranges::fold_meow` is overconstrained

Section: 26.6.18 [alg.fold] Status: New Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24

Priority: 3

View other active issues in [alg.fold].

View all other issues in [alg.fold].

View all issues with New status.

Discussion:

The two `convertible_to` constraints required by `ranges::fold_meow` mainly check whether the decayed result type of the binary operator can be constructed by both the invoke type and the initial value type.

However, using `constructible_from` seems more appropriate here because we don't need to care whether the two types can be converted implicitly and explicitly. Taking `string` and `string_view` as examples, the former can only be explicitly constructed by the latter and can be assigned by the latter, which makes `ranges::fold_meow` unable to fold ranges whose elements are of type `string` with an initial value of type `string_view`:

>     vector<string> vs{"a", "b", "c"};
>     string_view init{"d"};
>     auto result = ranges::fold_right(vs, init, plus{}); // still ill-formed after P2591R5 as the constraint is not satisfied

In addition, the two `movable` constraints in the function signature seem to be too strict as well since we only need to check that the decayed result type and the initial type can be move-constructible (one for returned by elidable move and one for move into other overloads) instead of whether they can be move-assignable.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. "NAD. Constraining algorithms is not about limiting the constraints to the bare minimum required by implementation details. That's what the C++0x attempt did - in an effort to maintain backward compatibility - and it's a mess. While the `string`/`string_view` case might look superficially appealing, it would also allow `vector<string>`/`int`, which is much less so."

Proposed resolution:

This wording is relative to N4981.

1. Modify 26.4 [algorithm.syn], header `<algorithm>` synopsis, as indicated:

   >     #include <initializer_list>     // see 17.11.2 [initializer.list.syn]

   >     namespace std {
   >       […]
   >       namespace ranges {
   >         […]
   >         template<class F, class T, class I, class U>
   >           concept indirectly-binary-left-foldable-impl =  // exposition only
   >             movablemove_constructible<T> && movablemove_constructible<U> &&
   >             convertible_toconstructible_from<T, U, T> && invocable<F&, U, iter_reference_t<I>> &&
   >             assignable_from<U&, invoke_result_t<F&, U, iter_reference_t<I>>>;

   >         template<class F, class T, class I>
   >           concept indirectly-binary-left-foldable =      // exposition only
   >             copy_constructible<F> && indirectly_readable<I> &&
   >             invocable<F&, T, iter_reference_t<I>> &&
   >             convertible_toconstructible_from<decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>,
   >                    decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>> &&
   >             indirectly-binary-left-foldable-impl<F, T, I,
   >                             decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>;
   >         […]
   >       }
   >       […]
   >     }

2. Modify 26.6.18 [alg.fold] as indicated:

   >     template<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>,
   >              indirectly-binary-right-foldable<T, I> F>
   >       constexpr auto ranges::fold_right(I first, S last, T init, F f);
   >     template<bidirectional_range R, class T = range_value_t<R>,
   >              indirectly-binary-right-foldable<T, iterator_t<R>> F>
   >       constexpr auto ranges::fold_right(R&& r, T init, F f);

   > > -3- Effects: Equivalent to:

   > > >     using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>;
   > > >     if (first == last)
   > > >       return U(std::move(init));
   > > >     I tail = ranges::next(first, last);
   > > >     U accum =(invoke(f, *--tail, std::move(init)));
   > > >     while (first != tail)
   > > >       accum = invoke(f, *--tail, std::move(accum));
   > > >     return accum;

   > > […]

   >     template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>,
   >              indirectly-binary-left-foldable<T, I> F>
   >       constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
   >     template<input_range R, class T = range_value_t<R>,
   >              indirectly-binary-left-foldable<T, iterator_t<R>> F>
   >       constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);

   > > -6- Let `U` be `decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>`.

   > > -7- Effects: Equivalent to:

   > > >     if (first == last)
   > > >       return {std::move(first), U(std::move(init))};
   > > >     U accum =(invoke(f, std::move(init), *first));
   > > >     for (++first; first != last; ++first)
   > > >       accum = invoke(f, std::move(accum), *first);
   > > >     return {std::move(first), std::move(accum)};

   > > -8- Remarks: The return type is `fold_left_with_iter_result<I, U>` for the first overload and `fold_left_with_iter_result<borrowed_iterator_t<R>, U>` for the second overload.

### 4097(i). `views::reverse` should be specialized for some view types

Section: 25.7.21.1 [range.reverse.overview] Status: LEWG Submitter: Hewill Kang Opened: 2024-05-09 Last modified: 2024-06-24

Priority: 3

View all issues with LEWG status.

Discussion:

Currently, when `views::reverse` is applied to `empty_view`, `single_view`, or `repeat_view`, a `reverse_view` with the iterator type `reverse_iterator` will be produced.

Such an amount of instantiation seems less necessary for these three simple view types, since returning a copy of them is sufficient.

Note that this also makes `views::repeat(0) | views::reverse | views::take(5)` no longer timeout, which seems to be an improvement.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. Send to LEWG for review. The `repeat` part is related to LWG 4019(i).

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.21.1 [range.reverse.overview] as indicated:

   > -1- `reverse_view` takes a bidirectional view and produces another view that iterates the same elements in reverse order.

   > -2- The name `views::reverse` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Given a subexpression `E`, the expression `views::reverse(E)` is expression-equivalent to:

   > 1. (2.1) — If the type of `E` is a (possibly cv-qualified) specialization of `reverse_view`, equivalent to `E.base()`.

   > 2. (2.?) — Otherwise, if the type of `E` is a (possibly cv-qualified) specialization of `empty_view` (25.6.2.2 [range.empty.view]), `single_view` (25.6.3.2 [range.single.view]), or `repeat_view` (25.6.5.2 [range.repeat.view]), equivalent to `decay-copy(E)`.

   > 3. (2.2) — Otherwise, if the type of `E` is cv `subrange<reverse_iterator<I>, reverse_iterator<I>, K>` for some iterator type `I` and value `K` of type `subrange_kind`,

   >    1. (2.2.1) — if `K` is `subrange_kind::sized`, equivalent to:

   >       >     subrange<I, I, K>(E.end().base(), E.begin().base(), E.size())

   >    2. (2.2.2) — otherwise, equivalent to:

   >       >     subrange<I, I, K>(E.end().base(), E.begin().base())

   >    However, in either case `E` is evaluated only once.

   > 4. (2.3) — Otherwise, equivalent to `reverse_view{E}`.

### 4099(i). The simple case of `views::as_rvalue` and `views::common` are not strictly correct

Section: 25.7.7.1 [range.as.rvalue.overview], 25.7.20.1 [range.common.overview] Status: New Submitter: Hewill Kang Opened: 2024-05-10 Last modified: 2024-06-24

Priority: 4

View all other issues in [range.as.rvalue.overview].

View all issues with New status.

Discussion:

Currently, these two adaptors return `views::all(E)` when the type of subexpression `E` models `common_range` or its reference is the same as the rvalue reference.

However, the current wording does not require that a `const`-qualified object of a `range` type must have the same non-commonality or value category of the reference as the non-`const` one, this makes it theoretically possible for these two to still return non-common ranges or ranges whose reference are lvalue when acting on a `const`-qualified lvalue `view` object, because `views::all(E)` will remove the `const`-qualifier.

The proposed wording pedantically checks `views::all_t<decltype((E))>` instead of `decltype((E))`.

[2024-06-24; Reflector poll]

Set priority to 4 after reflector poll. "Not aware of any use case for making `const` affect those properties".

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.7.1 [range.as.rvalue.overview] as indicated:

   > -2- The name `views::as_rvalue` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` be an expression and let `T` be `decltype((E))`. The expression `views::as_rvalue(E)` is expression-equivalent to:

   > 1. (2.1) — `views::all(E)` if `same_as<range_rvalue_reference_t<views::all_t<T>>, range_reference_t<views::all_t<T>>>` is `true`.

   > 2. (2.2) — Otherwise, `as_rvalue_view(E)`.

2. Modify 25.7.20.1 [range.common.overview] as indicated:

   > -3- The name `views::common` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Given a subexpression `E`, the expression `views::common(E)` is expression-equivalent to:

   > 1. (3.1) — `views::all(E)`, if `decltype((E))``views::all_t<T>` models `common_range` and `views::all(E)` is a well-formed expression.

   > 2. (3.2) — Otherwise, `common_view{E}`.

### 4100(i). Default arguments and signatures of standard library non-member functions

Section: 16.4.6.4 [global.functions] Status: New Submitter: Jiang An Opened: 2024-05-11 Last modified: 2024-08-02

Priority: 3

View all other issues in [global.functions].

View all issues with New status.

Discussion:

Currently, `std::from_chars` and `std::to_chars` are specified with default arguments. Some implementors want to split them into more overloads to determine the base 10 at compile time (LLVM issue #91268). However, the current standard wording doesn't clearly allow such technique.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll. "It's a lot of new text just to say you can't do that, lose the example?" "Might be NAD, is this already disallowed?" "Only for member functions."

Proposed resolution:

This wording is relative to N4981.

1. Modify 16.4.6.4 [global.functions] as indicated:

   > […]

   > -2- A call to a non-member function signature described in 17 [support] through 32 [thread] and D [depr] shall behave as if the implementation declared no additional non-member function signatures. However, for each non-member function specified with at least one default argument, an implementation may declare additional function signatures with one or more trailing parameters that have default arguments removed.

   > [Example 1: The following program is possibly ill-formed because of ambiguity in overload resolution.

   > >     #include <cstddef>
   > >     #include <string>

   > >     // One overload of std::stoi ([string.conversions]) is specified as
   > >     // int stoi(const string& str, size_t* idx = nullptr, int base = 10);
   > >     // An implementation can declare these overloads instead of using default arguments:
   > >     // int stoi(const string& str, size_t* idx, int base);
   > >     // int stoi(const string& str, size_t* idx);
   > >     // int stoi(const string& str);

   > >     namespace usr {
   > >       int stoi(const std::string&);
   > >       int stoi(const std::string&, std::size_t*);
   > >     }

   > >     int main() {
   > >       using std::stoi;
   > >       using usr::stoi;
   > >       int (*p1)(const std::string&) = stoi;                 // possibly ill-formed
   > >       int (*p2)(const std::string&, std::size_t*) = stoi;   // possibly ill-formed
   > >     }

   > — end example]

### 4101(i). LWG 117 loses the sign for negative NaN on some architectures

Section: 31.7.6.3.2 [ostream.inserters.arithmetic] Status: New Submitter: Jonathan Wakely Opened: 2024-05-10 Last modified: 2024-05-15

Priority: 3

View all other issues in [ostream.inserters.arithmetic].

View all issues with New status.

Discussion:

LWG 117(i) fixed insertion of a `float` into an ostream by requiring a cast to `double`. This gives a surprising result on RISC-V when inserting a negative NaN, because RISC-V floating-point instructions do not preserve the sign or payload of NaN values. This means that `std::cout << -std::numeric_limits<float>::quiet_NaN()` prints `"nan"` rather than `"-nan"` as most users probably expect.

11.3 section of the RISC-V ISA manual (20191213):

> Except when otherwise stated, if the result of a floating-point operation is NaN, it is the canonical NaN. The canonical NaN has a positive sign and all significand bits clear except the MSB, a.k.a. the quiet bit. For single-precision floating-point, this corresponds to the pattern 0x7fc00000.

This is allowed by IEEE 754 (2019), as per section 6.3:

> When either an input or result is a NaN, this standard does not interpret the sign of a NaN.

So it is standard-conforming for `static_cast<double>(val)` to lose the sign (and payload) of a NaN. This might also affect Apple M1 chips, if they use the ARMv8 default-NaN mode.

The current wording does not permit an implementation to use something like `std::copysign(static_cast<double>(val), std::signbit(val) ? -1.0 : +1.0)` to restore the sign bit. Should this be allowed? Maybe we should say that it's unspecified whether the cast to `double` preserves the sign of a NaN? If not, should we add a note about the surprising behaviour?

Previous resolution [SUPERSEDED]

This wording is relative to N4981.

1. Modify 31.7.6.3.2 [ostream.inserters.arithmetic] as indicated:

   > -3- When `val` is of type `float` the formatting conversion occurs as if it performed the following code fragment:

   >       bool failed = use_facet<
   >         num_put<charT, ostreambuf_iterator<charT, traits>>
   >           >(getloc()).put(*this, *this, fill(),
   >             static_cast<double>(val)).failed();

   > [Note ?: When `val` is a NaN value, the conversion to `double` can alter the sign and payload. — end note]

[2024-05-15; Reflector poll]

Set priority to 3 after reflector poll.

[2024-05-15; Peter Dimov provides improved wording]

Jonathan observes that the same problem exists for `operator<<(extended-floating-point-type)`.

Proposed resolution:

This wording is relative to N4981.

1. Modify 31.7.6.3.2 [ostream.inserters.arithmetic] as indicated:

   > -3- When `val` is of type `float` the formatting conversion occurs as if it performed the following code fragment:

   >       bool failed = use_facet<
   >         num_put<charT, ostreambuf_iterator<charT, traits>>
   >           >(getloc()).put(*this, *this, fill(),
   >             static_cast<double>(val)dval).failed();

   > where `dval` is `val` converted to type `double` as if by `static_cast<double>(val)`, except that the sign and payload of NaN values may be preserved rather than discarded.

### 4102(i). `string_view(Iter, Iter)` constructor breaks existing code

Section: 27.3.3.2 [string.view.cons] Status: New Submitter: Derek Zhang Opened: 2024-05-14 Last modified: 2024-08-02

Priority: 2

View other active issues in [string.view.cons].

View all other issues in [string.view.cons].

View all issues with New status.

Discussion:

As a result of the new constructor added by P1391, this stopped working in C++20:

    void fun(string_view);
    void fun(vector<string_view>);
    fun({"a", "b"});

Previously the first `fun` wasn't viable, so it constructed a `vector<string_view>` of two elements using its initializer-list constructor and then called the second `fun`. Now `{"a", "b"}` could also be a call to the new `string_view(Iter, Iter)`, so it's ambiguous and fails to compile.

The following case is arguably worse as it doesn't become ill-formed in C++20, it still compiles but now has undefined behaviour:

    fun({{"a", "b"}});

Previously the first `fun` wasn't viable, so this constructed a `vector<string_view>` of two elements (via somewhat bizarre syntax, but using the same initializer-list constructor as above). Now it constructs a `vector` from an `initializer_list` with one element, where that element is constructed from the two `const char*` using `string_view(Iter, Iter)`. But those two pointers are unrelated and do not form a valid range, so this violates the constructor's precondition and has undefined behaviour. If you're lucky it crashes at runtime when trying to reach `"b"` from `"a"`, but it could also form a `string_view` that reads arbitrary secrets from the memory between the two pointers.

[Jonathan comments]

At the very least, we should have an Annex C entry documenting the change. Making the new `string_view(Iter, Iter)` constructor `explicit` would prevent the runtime behaviour change for the second example, but GCC thinks the first example would still be ambiguous (it seems to depend on how list-initialization handles explicit constructors, which has implementation divergence).

Maybe we should have a deleted constructor matching string literals:

    template<size_t N1, size_t N2>
    basic_string_view(const charT(&)[N1], const charT(&)[N2]) = delete;

Or to handle both `const char[N]` and `char[N]`:

    template<class A1, class A2>
    requires (rank_v<A1> == 1) && (rank_v<A2> == 1)
    basic_string_view(A1&, A2&) = delete;

Both options would prevent this currently valid (but weird) code:

    const char arr[] = "str";
    std::string_view s(arr, arr); // s.size() == 0 and s.data() == arr

That seems acceptable, because `std::string_view s(arr, 0)` is simpler and clearer anyway.

[2024-08-02; Reflector poll]

Set priority to 2 after reflector poll. "The constructor should be made `explicit` as part of any resolution for this."

Proposed resolution:

### 4103(i). `ranges::unique_copy`'s constraints for the case where `result` is an `input_iterator` are not quite right

Section: 26.7.9 [alg.unique] Status: New Submitter: Hewill Kang Opened: 2024-05-14 Last modified: 2024-06-24

Priority: 3

View all other issues in [alg.unique].

View all issues with New status.

Discussion:

When `r` is only an `input_range` and `result` is also an `input_iterator`, `ranges::unique_copy` writes the elements of `r` into `result` and reads the value of `result` in the next iteration.

However, in this case, the function only requires that the `value_type` of `r` and the `value_type` of `result` are the same, which seems too loose because the `value_type` is not particularly useful in the ranges world compared to the `reference`, which is also reflected in the fact that the implementation applies the compare function on both dereferenced values (demo):

>     #include <algorithm>
>     #include <iostream>
>     #include <ranges>
>     #include <vector>

>     int main() {
>       auto r = std::views::istream<bool>(std::cin);
>       std::vector<bool> v(10);
>       auto proj = [](std::same_as<bool> auto b) { return b; }; // ban vector<bool>::reference
>       std::ranges::unique_copy(r, v.begin(), {}, proj);        // hard error in libstdc++, libc++ and MSVC-STL
>     }

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4981.

1. Modify 26.4 [algorithm.syn], header `<algorithm>` synopsis, as indicated:

   >     #include <initializer_list>     // see [initializer.list.syn]

   >     namespace std {
   >       […]
   >       namespace ranges {
   >         template<class I, class O>
   >           using unique_copy_result = in_out_result<I, O>;

   >         template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
   >                  indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
   >           requires indirectly_copyable<I, O> &&
   >                    (forward_iterator<I> ||
   >                     (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>
   >                      indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) ||
   >                     indirectly_copyable_storable<I, O>)
   >           constexpr unique_copy_result<I, O>
   >             unique_copy(I first, S last, O result, C comp = {}, Proj proj = {});
   >         template<input_range R, weakly_incrementable O, class Proj = identity,
   >                  indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
   >           requires indirectly_copyable<iterator_t<R>, O> &&
   >                    (forward_iterator<iterator_t<R>> ||
   >                     (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>
   >                      indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>,
   >                                                       projected<O, Proj>>) ||
   >                     indirectly_copyable_storable<iterator_t<R>, O>)
   >           constexpr unique_copy_result<borrowed_iterator_t<R>, O>
   >             unique_copy(R&& r, O result, C comp = {}, Proj proj = {});
   >       }
   >       […]
   >     }

2. Modify 26.7.9 [alg.unique] as indicated:

   >     template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
   >              indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
   >       requires indirectly_copyable<I, O> &&
   >                (forward_iterator<I> ||
   >                 (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>
   >                  indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) ||
   >                 indirectly_copyable_storable<I, O>)
   >       constexpr ranges::unique_copy_result<I, O>
   >         ranges::unique_copy(I first, S last, O result, C comp = {}, Proj proj = {});
   >     template<input_range R, weakly_incrementable O, class Proj = identity,
   >              indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
   >       requires indirectly_copyable<iterator_t<R>, O> &&
   >                (forward_iterator<iterator_t<R>> ||
   >                (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>
   >                 indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>,
   >                                                  projected<O, Proj>>) ||
   >                 indirectly_copyable_storable<iterator_t<R>, O>)
   >       constexpr ranges::unique_copy_result<borrowed_iterator_t<R>, O>
   >         ranges::unique_copy(R&& r, O result, C comp = {}, Proj proj = {});

   > > -6- Let `pred` be `equal_to{}` for the overloads in namespace `std` with no parameter `pred`, […]

### 4104(i). `basic_const_iterator<volatile int*>` is not a `contiguous_iterator`

Section: 24.5.3 [const.iterators] Status: New Submitter: Hewill Kang Opened: 2024-05-14 Last modified: 2024-06-24

Priority: 4

View other active issues in [const.iterators].

View all other issues in [const.iterators].

View all issues with New status.

Discussion:

Although `volatile int*` satisfies `contiguous_iterator`, due to the formula design of `iter_const_reference_t`, its result for the former will be `int`, which makes `basic_const_iterator<volatile int*>` no longer a `contiguous_iterator` even though its `iterator_concept` is defined as `contiguous_iterator_tag` and it has a valid `operator->()` that returns `const volatile int*`.

This seems to defeat the purpose of `basic_const_iterator` to preserve the behavior of the underlying iterator (except for indirection operators). The same goes for `basic_const_iterator<const volatile int*>`:

>     #include <iterator>
>     #include <span>

>     int main() {
>       int i = 42;

>       const volatile int* p = &i;
>       static_assert(std::contiguous_iterator<decltype(p)>);
>       std::span sp1{p, 1}; // ok

>       std::basic_const_iterator it{p};
>       static_assert(std::same_as<decltype(it.operator->()), const volatile int*>);
>       static_assert(std::same_as<decltype(it)::iterator_concept, std::contiguous_iterator_tag>);
>       static_assert(std::contiguous_iterator<decltype(it)>); // failed
>       std::span sp2{it, 1}; // failed
>     }

[2024-06-24; Reflector poll]

Set priority to 4 after reflector poll. See also LWG 3813(i).

Proposed resolution:

### 4107(i). Map formatter may conflict with user-defined specializations of `pair`/`tuple` formatters

Section: 28.5.7.4 [format.range.fmtmap] Status: New Submitter: Victor Zverovich Opened: 2024-05-18 Last modified: 2025-02-07

Priority: 3

View all issues with New status.

Discussion:

Consider the following example:

>     #include <format>
>     #include <map>
>     #include <print>

>     struct x {};

>     template<typename K>
>     struct std::formatter<std::pair<K, x>> : std::formatter<std::string_view> {
>       auto format(const std::pair<K, x>& p, auto& ctx) const {
>         return std::format_to(ctx.out(), "x/x");
>       }
>     };

>     int main() {
>       std::print("{}", std::map<x, x>());
>     }

It doesn't compile because the formatter for maps requires the element formatter to have `set_brackets` and `set_separator` (28.5.7.4 [format.range.fmtmap]):

>     underlying_.underlying().set_brackets({}, {});
>     underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));

The specialization `std::formatter<std::pair<K, x>>` itself is allowed according to 16.4.5.2.1 [namespace.std]:

> Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace `std` provided that

> - the added declaration depends on at least one program-defined type, and

> - the specialization meets the standard library requirements for the original template.

but it's unclear what exactly the part "the specialization meets the standard library requirements for the original template" means for this formatter. Does it mean that the specialization must provide `set_brackets` and `set_separator` and does the output have to be consistent with the main template? The latter would render the specialization useless. On the other hand if users are allowed to customize pair and tuple formatting the current specification of the map formatter is broken.

The correct resolution appears to be to not restrict user-defined formatter specializations of `pair`s and `tuple`s, and make map be responsible for its own structural formatting rather than delegating part of it to other formatters in an arbitrary way. This resolution has been applied to {fmt}'s implementation of range formatting to address #3685.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll. Several votes for NAD (a specialization must provide `set_brackets` etc.), and suggestions it's a design change that LEWG should see.

Proposed resolution:

This wording is relative to N4981.

1. Modify 28.5.8.3 [format.args] as indicated:

   > >     namespace std {
   > >       template<ranges::input_range R, class charT>
   > >       struct range-default-formatter<range_format::map, R, charT> {
   > >       private:
   > >         using maybe-const-map = fmt-maybe-const<R, charT>;            // exposition only
   > >         using element-type =                                          // exposition only
   > >           remove_cvref_t<ranges::range_reference_t<maybe-const-map>>;
   > >         range_formatter<element-type, charT> underlying_;             // exposition only
   > >         using key-type = tuple_element_t<0, element-type>;            // exposition only
   > >         using value-type = tuple_element_t<1, element-type>;          // exposition only
   > >         formatter<key-type, charT> key-formatter_;                    // exposition only
   > >         formatter<value-type, charT> value-formatter_;                // exposition only

   > >       public:
   > >         constexpr range-default-formatter();

   > >         template<class ParseContext>
   > >           constexpr typename ParseContext::iterator
   > >             parse(ParseContext& ctx);

   > >         template<class FormatContext>
   > >           typename FormatContext::iterator
   > >             format(maybe-const-map& r, FormatContext& ctx) const;
   > >       };
   > >     }

   >     constexpr range-default-formatter();

   > > -1- Mandates: Either:

   > > 1. (1.1) — `element-type` is a specialization of `pair`, or

   > > 2. (1.2) — `element-type` is a specialization of `tuple` and `tuple_size_v<element-type> == 2`.

   > > -2- Effects: Equivalent to:

   > > >     underlying_.set_brackets(STATICALLY-WIDEN<charT>("{"), STATICALLY-WIDEN<charT>("}"));
   > > >     underlying_.underlying().set_brackets({}, {});
   > > >     underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));

   >     template<class ParseContext>
   >       constexpr typename ParseContext::iterator
   >         parse(ParseContext& ctx);

   > > -3- Effects: Equivalent to: `return underlying_.parse(ctx);` Parses the format specifiers as a range-format-spec and stores the parsed specifiers in `*this`.

   > > If `key-formatter_.set_debug_format()` is a valid expression, and there is no range-underlying-spec, then calls `key-formatter_.set_debug_format()`.

   > > If `value-formatter_.set_debug_format()` is a valid expression, and there is no range-underlying-spec, then calls `value-formatter_.set_debug_format()`.

   > > -?- Returns: An iterator past the end of the range-format-spec.

   >     template<class FormatContext>
   >       typename FormatContext::iterator
   >         format(maybe-const-map& r, FormatContext& ctx) const;

   > > -4- Effects: Equivalent to: `return underlying_.format(r, ctx);` Writes the following into `ctx.out()`, adjusted according to the range-format-spec:

   > > 1. — `STATICALLY-WIDEN<charT>("{")` unless the `n` option is specified,

   > > 2. — for each element `e` of the range `r`:

   > >    1. — the result of writing `get<0>(e)` via `key-formatter_`,

   > >    2. — `STATICALLY-WIDEN<charT>(": ")`,

   > >    3. — the result of writing `get<1>(e)` via `value-formatter_`,

   > >    4. — `STATICALLY-WIDEN<charT>(", ")`, unless `e` is the last element of `r`, and

   > > 3. — `STATICALLY-WIDEN<charT>("}")` unless the `n` option is specified.

   > > -?- Returns: An iterator past the end of the output range.

### 4108(i). `lazy_split_view` should be `sized_range` when pattern is empty `tiny-range`

Section: 25.7.16.2 [range.lazy.split.view], 25.7.17.2 [range.split.view] Status: SG9 Submitter: Hewill Kang Opened: 2024-05-23 Last modified: 2024-08-02

Priority: 4

View other active issues in [range.lazy.split.view].

View all other issues in [range.lazy.split.view].

View all issues with SG9 status.

Discussion:

When the pattern range is empty, `lazy_split_view` will split each element into individual subranges, which means its size is equal to the size of the underlying range.

Since we already have a `tiny-range` that can determine whether the range is empty by its type, it seems valuable to provide a `size` for `lazy_split_view` in this case, given that we already specifically checked for it by `Pattern::size() == 0` in `inner-iterator::operator++()`.

[2024-08-02; Reflector poll]

Set priority to 4 after reflector poll. Set Status to SG9. "Design change". "`tiny-range` should accept `span<T, 0|1>` or `ref_view<array<T, 0|1>>`, see related paper P1419."

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.16.2 [range.lazy.split.view] as indicated:

   >     namespace std::ranges {
   >       template<auto> struct require-constant;                        // exposition only

   >       template<class R>
   >       concept tiny-range =                                           // exposition only
   >          sized_range<R> &&
   >          requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   >          (remove_reference_t<R>::size() <= 1);

   >       template<input_range V, forward_range Pattern>
   >         requires view<V> && view<Pattern> &&
   >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
   >                  (forward_range<V> || tiny-range<Pattern>)
   >       class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> {
   >         […]
   >         constexpr auto size()
   >           requires sized_range<V> &&
   >                    tiny-range<Pattern> && (Pattern::size() == 0) {
   >           return ranges::size(base_);
   >         }

   >         constexpr auto size() const
   >           requires sized_range<const V> &&
   >                    tiny-range<Pattern> && (Pattern::size() == 0) {
   >           return ranges::size(base_);
   >         }
   >       };
   >       […]
   >     }

2. Modify 25.7.17.2 [range.split.view] as indicated:

   >     namespace std::ranges {
   >       template<forward_range V, forward_range Pattern>
   >         requires view<V> && view<Pattern> &&
   >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to>
   >       class split_view : public view_interface<split_view<V, Pattern>> {
   >         […]
   >         constexpr auto size()
   >           requires sized_range<V> &&
   >                    tiny-range<Pattern> && (Pattern::size() == 0) {
   >           return ranges::size(base_);
   >         }

   >         constexpr auto size() const
   >           requires sized_range<const V> &&
   >                    tiny-range<Pattern> && (Pattern::size() == 0) {
   >           return ranges::size(base_);
   >         }
   >       };
   >       […]
   >     }

### 4110(i). `shared_ptr(nullptr_t, Deleter)` is overconstrained, breaking some sensible deleters

Section: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: Louis Dionne Opened: 2024-06-11 Last modified: 2025-10-22

Priority: 3

View other active issues in [util.smartptr.shared.const].

View all other issues in [util.smartptr.shared.const].

View all issues with New status.

Discussion:

The following code doesn't compile on conforming implementations:

        #include <memory>

        void f() {
            std::shared_ptr<int>(new int, [](auto pointer) { delete pointer; });
        }

(Godbolt)

This is caused by the constraint on `shared_ptr(nullptr_t p, D d);` being that `d(p)` is valid (20.3.2.2.2 [util.smartptr.shared.const] p9), which leads to a hard error inside the lambda since it is called with a `nullptr_t`. This seems unintended.

See LLVM issue 93071 comment for additional context.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

"I don't agree with the proposed resolution. As a general principle, `shared_ptr<T>(p, d)` always calls `d(p)` and never `d(static_cast<T*>(p))`.
If we really want to make this work, which is not unreasonable, even though the fix on the user side is trivial, we should make the `nullptr_t` constructor templated on `same_as<nullptr_t>` (or `convertible_to<nullptr_t>`?)."

"That would break passing `NULL`, only `nullptr` would work. It can be made to work by checking that `d(p)` is well-formed in a function parameter with a default argument, instead of as a template parameter:"

    struct shared_ptr {
      template<class Y, class D>
      shared_ptr(Y* p, D d, std::void_t<decltype(d(p))>* = nullptr) {}
      template<class D>
      shared_ptr(std::nullptr_t p, D d, std::void_t<decltype(d(p))>* = nullptr) {}
    };
    shared_ptr s(new int, [](auto p) {delete p;});

"Ugh. We don't have to use it everywhere, only these two specific constructors."

Proposed resolution:

This wording is relative to N4981.

1. In 20.3.2.2.2 [util.smartptr.shared.const]/9, specify that `shared_ptr(nullptr_t p, D d);` checks whether `d(static_cast<T*>(nullptr))` is well-formed. This requires expressing the the constraints for the `Y*` constructors and the `nullptr_t` constructors separately, which is mostly editorial:

   >     template<class Y, class D> shared_ptr(Y* p, D d);
   >     template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
   >     template<class D> shared_ptr(nullptr_t p, D d);
   >     template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

   > -9- Constraints: `is_move_constructible_v<D>` is `true`, and `d(p)` is a well-formed expression. For the first two overloads:

   > 1. (9.1) If `T` is an array type, then either `T` is `U[N]` and `Y(*)[N]` is convertible to `T*`, or `T` is `U[]` and `Y(*)[]` is convertible to `T*`.
   > 2. (9.2) If `T` is not an array type, then `Y*` is convertible to `T*`.

   >     template<class D> shared_ptr(nullptr_t p, D d);
   >     template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

   > -?- Constraints: `is_move_constructible_v<D>` is `true`, and `d(static_cast<T*>(p))` is a well-formed expression.

### 4111(i). LWG 270 and ranges version of binary search algorithms

Section: 26.8.4 [alg.binary.search], 26.8.1 [alg.sorting.general] Status: New Submitter: Jiang An Opened: 2024-06-18 Last modified: 2024-06-24

Priority: 3

View all other issues in [alg.binary.search].

View all issues with New status.

Discussion:

LWG 270(i) relaxed the requirements for `comp` of binary search algorithms (26.8.4 [alg.binary.search]). However, the relaxation doesn't seem automatically applied to `ranges` versions, because `ranges` versions of these algorithms constrain the `Comp` types with `indirect_strict_weak_order`.

Perhaps we should additionally say some semantic requirements of `indirect_strict_weak_order` aren't imposed for these algorithms or change the constraints.

[St. Louis 2024-06-24; set priority to P3]

Proposed resolution:

### 4114(i). `elements_view::iterator::operator*` missing conditional `noexcept` specification

Section: 25.7.23.3 [range.elements.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-02 Last modified: 2024-08-02

Priority: 3

View other active issues in [range.elements.iterator].

View all other issues in [range.elements.iterator].

View all issues with New status.

Discussion:

`views::elements`, which can be seen as a specialization of `views::transform`, is used to transform the original tuple into its elements.

Since neither has a specialization for `iter_move`, the customization point is dispatched to the default implementation. However, unlike the latter, `elements_view::iterator::operator*` does not have a `noexcept` specification, which makes calls to `iter_move` never `noexcept`, which seems to be an oversight (demo):

>     #include <ranges>
>     #include <vector>

>     std::vector v{std::pair{1, "a"}, {2, "b"}};

>     auto r1 = v | std::views::keys;
>     auto i1 = r1.begin();
>     static_assert(noexcept(*i1));                        // failed
>     static_assert(noexcept(std::ranges::iter_move(i1))); // failed

>     auto get_key = [](auto& t) noexcept -> auto& { return std::get<0>(t); };
>     auto r2 = v | std::views::transform(get_key);
>     auto i2 = r2.begin();
>     static_assert(noexcept(*i2));
>     static_assert(noexcept(std::ranges::iter_move(i2)));

The proposed resolution is aligned with the strengthened implementation of MSVC-STL.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.23.3 [range.elements.iterator] as indicated:

   >     namespace std::ranges {
   >       template<input_range V, size_t N>
   >         requires view<V> && has-tuple-element<range_value_t<V>, N> &&
   >                  has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
   >                  returnable-element<range_reference_t<V>, N>
   >       template<bool Const>
   >       class elements_view<V, N>::iterator {
   >         […]

   >         static constexpr decltype(auto) get-element(const iterator_t<Base>& i);     // exposition only
   >           noexcept(noexcept(std::get<N>(*i))) requires is_reference_v<range_reference_t<Base>> {
   >           return std::get<N>(*i);
   >         }

   >         static constexpr decltype(auto) get-element(const iterator_t<Base>& i)      // exposition only
   >           noexcept(noexcept(std::get<N>(*i)) &&
   >                    is_nothrow_move_constructible_v<tuple_element_t<N, range_reference_t<Base>>>) {
   >           using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
   >           return static_cast<E>(std::get<N>(*i));
   >         }

   >       public:
   >         […]

   >         constexpr decltype(auto) operator*() const
   >           noexcept(noexcept(get-element(current_)))
   >         { return get-element(current_); }

   >         […]
   >       };
   >     }

   […]

       static constexpr decltype(auto) get-element(const iterator_t<Base>& i);

   -3- Effects: Equivalent to:

   >     if constexpr (is_reference_v<range_reference_t<Base>>) {
   >       return std::get<N>(*i);
   >     } else {
   >       using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
   >       return static_cast<E>(std::get<N>(*i));
   >     }

### 4115(i). `move_iterator::operator*` should have conditional `noexcept` specification

Section: 24.5.4.6 [move.iter.elem] Status: New Submitter: Hewill Kang Opened: 2024-07-03 Last modified: 2024-08-02

Priority: 4

View all other issues in [move.iter.elem].

View all issues with New status.

Discussion:

For `move_iterator`, dereferencing it is actually equivalent to applying `iter_move` to it.

However, unlike the latter, `move_iterator`'s dereference operator lacks a `noexcept` specification, which seems to be an oversight given that the standard goes to such great lengths to preserve the noexceptness of `iter_move`, and the main purpose of `move_iterator` is precisely to apply `iter_move` to the underlying iterator via dereferencing.

[2024-08-02; Reflector poll]

Set priority to 4 after reflector poll. "Do we have evidence conditional noexcept matters here? Do we have a policy that `operator*` should be noexcept whenever possible? What criteria are we using to decide here?"

Proposed resolution:

This wording is relative to N4981.

1. Modify 24.5.4.2 [move.iterator] as indicated:

   >     namespace std {
   >       template<class Iterator>
   >       class move_iterator {
   >       public:
   >         […]
   >         constexpr reference operator*() const noexcept(noexcept(ranges::iter_move(current)));
   >         […]
   >       };
   >     }

2. Modify 24.5.4.6 [move.iter.elem] as indicated:

   >     constexpr reference operator*() const noexcept(noexcept(ranges::iter_move(current)));

   > > -1- Effects: Equivalent to: `return ranges::iter_move(current);`

### 4116(i). `enumerate_view::iterator` and `cartesian_product_view::iterator` should not always provide `iterator_category`

Section: 25.7.24.3 [range.enumerate.iterator], 25.7.33.3 [range.cartesian.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-07 Last modified: 2025-10-21

Priority: 3

View all other issues in [range.enumerate.iterator].

View all issues with New status.

Discussion:

These two iterators do not support `*r++` for non-forward iterators, so we should not provide `iterator_category` as they are not C++17 iterators.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Six votes for Tentatively Ready, but not unanimous.

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.7.24.3 [range.enumerate.iterator] as indicated:

   >     namespace std::ranges {
   >       template<view V>
   >         requires range-with-movable-references<V>
   >       template<bool Const>
   >       class enumerate_view<V>::iterator {
   >         using Base = maybe-const<Const, V>;                         // exposition only

   >       public:
   >         using iterator_category = input_iterator_tag;      // present only if Base
   >                                                            // models forward_range
   >         using iterator_concept = see below;
   >         […]
   >       };
   >     }

2. Modify 25.7.33.3 [range.cartesian.iterator] as indicated:

   >     namespace std::ranges {
   >       template<input_range First, forward_range... Vs>
   >         requires (view<First> && ... && view<Vs>)
   >       template<bool Const>
   >       class cartesian_product_view<First, Vs...>::iterator {
   >       public:
   >         using iterator_category = input_iterator_tag;      // present only if maybe-const<Const, First>
   >                                                            // models forward_range
   >         using iterator_concept  = see below;
   >         […]
   >       };
   >     }

### 4117(i). `generator::iterator` should provide `iterator_concept`

Section: 25.8.6 [coro.generator.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-07 Last modified: 2024-08-02

Priority: 4

View other active issues in [coro.generator.iterator].

View all other issues in [coro.generator.iterator].

View all issues with New status.

Discussion:

`generator::iterator` currently does not provide an `iterator_concept`. While this does not affect it being an `input_iterator`, providing `iterator_concept` does improve consistency given that other C++20 iterators have such a member type alias.

[2024-08-02; Reflector poll]

Set priority to 4 after reflector poll. Six votes for P0 (tentatively ready) but one NAD vote saying that if this has no effect on the category then there's no reason to add it.

Proposed resolution:

This wording is relative to N4981.

1. Modify 25.8.6 [coro.generator.iterator] as indicated:

   >     namespace std {
   >       template<class Ref, class V, class Allocator>
   >       class generator<Ref, V, Allocator>::iterator {
   >       public:
   >         using iterator_concept = input_iterator_tag;
   >         using value_type = value;
   >         using difference_type = ptrdiff_t;
   >         […]
   >       };
   >     }

### 4118(i). How should `duration` formatters format custom `rep` types?

Section: 30.12 [time.format] Status: New Submitter: Jonathan Wakely Opened: 2024-07-08 Last modified: 2024-07-31

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with New status.

Discussion:

The `formatter<chrono::duration<Rep, Period>, charT>` partial specialization needs to be able to format the `Rep` type, because the `%Q` conversion specifier says to format the value returned by `.count()` which is of type `Rep`. This implies that the `Rep` type must be formattable, although the precise method of formatting it is not specified. Presumably either `format("{}", d.count())` or `ostrm << d.count()` needs to work.

28.5.6.4 [format.formatter.spec] p2 (2.3) says:

> For each `charT`, for each cv-unqualified arithmetic type `ArithmeticT` other than `char`, `wchar_t`, `char8_t`, `char16_t`, or `char32_t`, a specialization: `template<> struct formatter<ArithmeticT, charT>;`

However, nothing prevents the excluded types being used as the `rep` for a `chrono::duration`. This means you can use `chrono::duration<wchar_t>` and `chrono::duration<char8_t>` as durations, but you can't format them to `char` strings.

I think only the `%Q` conversion specifier formats the `rep` type directly (without converting durations to formattable types like `hours` or `seconds`), and so I don't think this problem exists for other chrono `formatter` specializations, because `%Q` can only be used for durations (that's not entirely clear, since `%q` and `%Q` are specified to format "the duration's unit suffix" and "the duration's numeric value", but presumably that means they can only be used for `duration` types).

Should the specialization of `formatter` for `chrono::duration` be constrained to require that the `rep` type can be formatted? Or should the `%Q` conversion specifier say that the numeric value is formatted by inserting into an `ostream` (which would treat `wchar_t` and `char8_t` rep types as characters, not integers)? Or should `%Q` say that the numeric value is converted to an integral type, which we know how to format?

This is somewhat related to issue 953(i), since it's unclear which operations "a class emulating an arithmetic type" needs to support.

[2024-07-31; Reflector poll]

Set priority to 3 after reflector poll.

"Don't convert to an integer type, that would be wrong for `duration<long double>` and could overflow for `duration<BigInt>`."

"`%Q` could format using `+d.count()`"

Some requests to disallow using code unit types as duration reps, e.g. `duration<char>`. Alternatively it just shouldn't be formattable. Alternatively, don't bother preventing dumb things.

There's a similar issue in `operator<<` for `duration`, which writes `d.count()` to the stream. For a custom rep type that might be ill-formed. For character types it might print as a character not an integer.

Proposed resolution:

This wording is relative to N4986.

1. Modify 30.2 [time.syn] as indicated:

   >     namespace std {
   >       template<class Rep, class Period, class charT>
   >         requires formattable<Rep, charT>
   >         struct formatter<chrono::duration<Rep, Period>, charT>;
   >       template<class Duration, class charT>
   >         struct formatter<chrono::sys_time<Duration>, charT>;

### 4120(i). `move_iterator` should provide `iterator_category` only when it models `forward_iterator`

Section: 24.5.4.2 [move.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-12 Last modified: 2024-08-31

Priority: 3

View all other issues in [move.iterator].

View all issues with New status.

Discussion:

Following up LWG 4116(i), when the underlying iterator does not satisfy `forward_iterator`, `move_iterator::operator++(int)` will return `void`, which fails to meet the C++17 iterator requirements, which should not provide `iterator_category`.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll.

"NAD, would be a major breaking change to something present since C++11."

"We're knowingly breaking the rules here for backwards compatibility. Should have a note here indicating that the rule is broken for good reason, and that you should do as the Standard says and not as the Standard does."

Proposed resolution:

This wording is relative to N4986.

1. Modify 24.5.4.2 [move.iterator] as indicated:

   > [Drafting note: `Iterator` that models `forward_iterator` implies that `iterator_traits<Iterator>::iterator_category` is always valid as it satisfies the exposition-only concept `cpp17-iterator`.]

   > -2- The member typedef-name `iterator_category` is defined if and only if the qualified-id `iterator_traits<Iterator>::iterator_category` is valid and denotes a type `Iterator` models `forward_iterator`. In that case, `iterator_category` denotes

   > 1. (2.1) — `random_access_iterator_tag` if the type `iterator_traits<Iterator>::iterator_category` models `derived_from<random_access_iterator_tag>`, and

   > 2. (2.2) — `iterator_traits<Iterator>::iterator_category` otherwise.

### 4123(i). Container effects use "the assignment operator or move assignment operator"

Section: 23.3.5.4 [deque.modifiers], 23.3.13.5 [vector.modifiers], 23.3.16.5 [inplace.vector.modifiers] Status: New Submitter: Jonathan Wakely Opened: 2024-07-25 Last modified: 2024-12-06

Priority: 3

View other active issues in [deque.modifiers].

View all other issues in [deque.modifiers].

View all issues with New status.

Discussion:

The spec for `deque::erase` talks about a exception "thrown by the assignment operator of `T`" but it's unclear which assignment operator this means. Arguably, this is fine because it means "the assignment operator that is used when repositioning the remaining elements". However, `deque::append_range`, `vector::append_range`, `vector::erase`, `inplace_vector::append_range`, and `inplace_vector::erase` talk about "the assignment operator or move assignment operator" which is just odd. In C++03 this just said "the assignment operator" and move semantics added "or the move assignment operator" but we could improve it.

What we should talk about is "an assignment operator", or "the assignment operator selected by overload resolution for the assignment expressions performed by the operation", or something like that.

This is potentially a bigger issue than just assignment: for `append_range` we say "If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator [...]" and there's no guarantee that the constructor used for initializing a Cpp17CopyInsertable type is a copy constructor or move constructor. It could be some templated constructor that is a better match than any of the special member functions.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll. Arthur to draft wording.

[2024-12-06; LWG telecon]

23.3.11.4 [list.modifiers] p1 says:

> Complexity: Insertion of a single element into a list takes constant time and exactly one call to a constructor of `T`. Insertion of multiple elements into a list is linear in the number of elements inserted, and the number of calls to the copy constructor or move constructor of `T` is exactly equal to the number of elements inserted.

In addition to incorrectly talking about "the copy constructor or move constructor", it should not should not talk about any "call to a constructor" because scalars do not have constructors at all. We should talk about calls to `allocator_traits::construct` not constructors, or objects being constructed.

Similarly, p5 says:

> Complexity: Erasing a single element is a constant time operation with a single call to the destructor of `T`. Erasing a range in a list is linear time in the size of the range and the number of calls to the destructor of type `T` is exactly equal to the size of the range.

This should talk about calls to `allocator_traits::destroy`, or objects being destroyed.

23.3.5.4 [deque.modifiers] is similar. Look for similar problems elsewhere.

[2024-12-06; Jonathan adds wording, incorporating Arthur's wording]

Proposed resolution:

This wording is relative to N4993.

1. Modify 23.3.5.3 [deque.capacity] as indicated:

   >     void shrink_to_fit();

   > -5- Preconditions: `T` is CppMoveInsertable into `deque`.

   > -6- Effects: `shrink_to_fit` is a non-binding request to reduce memory use but does not change the size of the sequence. [Note 1: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] If the size is equal to the old capacity, or if an exception is thrown other than by the move constructor move-construction of one object of a non-Cpp17CopyInsertable type `T` from another, then there are no effects.

2. Modify 23.3.5.4 [deque.modifiers] as indicated:

   >     iterator insert(const_iterator position, const T& x);
   >     ...
   >     template<container-compatible-range<T> R>
   >       void append_range(R&& rg);

   > -1- Effects: [...]

   > -2- Complexity: The complexity is linear in the number of elements inserted plus the lesser of the distances to the beginning and end of the deque. Inserting a single element at either the beginning or end of a deque always takes constant time and causes a single call to a constructor of T constructs a single object of type `T`.

   > -3- Remarks: If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of `T` construction or assignment of one object of type `T` from another , there are no effects. If an exception is thrown while inserting a single element at either end, there are no effects. Otherwise, if an exception is thrown by the move constructor of a move-construction of one object of non-Cpp17CopyInsertable type `T` from another, the effects are unspecified.

   > [...]

   > -5- Throws: Nothing unless an exception is thrown by the assignment operator of `T` the assignment of one object of type `T` from another .

   >     iterator erase(const_iterator position);
   >     iterator erase(const_iterator first, const_iterator last);
   >     void pop_front();
   >     void pop_back();

   > -4- Effects: An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.

   > [Note 1: `pop_front` and `pop_back` are erase operations. — end note]

   > -5- Throws: Nothing unless an exception is thrown by thean assignment operator of `T`.

   > -6- Complexity: The number of calls to the destructor of `T` objects of type `T` destroyed is the same as the number of elements erased, but the number of calls to the assignment operator of `T` is no more than the lesser of the number of elements before the erased elements and the number of elements after the erased elements.

3. Modify 23.3.7.5 [forward.list.modifiers] as indicated:

   > -1- [...] Inserting `n` elements into a `forward_list` is linear in `n`, and the number of calls to the copy or move constructor of objects of type `T` constructed is exactly equal to `n`. Erasing `n` elements from a `forward_list` is linear in `n` and the number of calls to the destructor of objects of type `T` destroyed is exactly equal to `n`.

4. Modify 23.3.11.4 [list.modifiers] as indicated:

   > -1- Complexity: Insertion of a single element into a list takes constant time and constructs exactly one call to a constructor of `T` object of type `T`. Insertion of multiple elements into a list is linear in the number of elements inserted and the number of calls to the copy constructor or move constructor of `T` objects of type `T` constructed is exactly equal to the number of elements inserted.

   > -2- Remarks: Does not affect the validity of iterators and references. If an exception is thrown, there are no effects.

   >     iterator erase(const_iterator position);
   >     iterator erase(const_iterator first, const_iterator last);
   >     void pop_front();
   >     void pop_back();
   >     void clear() noexcept;

   > -3- Effects: Invalidates only the iterators and references to the erased elements.

   > -4- Throws: Nothing.

   > -5- Complexity: Erasing a single element is a constant time operation with a single call to the destructor of `T` object of type `T` destroyed. Erasing a range in a list is linear time in the size of the range and the number of calls to the destructor of type `T` objects of type `T` destroyed is exactly equal to the size of the range.

5. Modify 23.3.13.2 [vector.cons] as indicated:

   >     template<class InputIterator>
   >       constexpr vector(InputIterator first, InputIterator last,
   >                        const Allocator& = Allocator());

   > -9- Effects: Constructs a `vector` equal to the range [`first`, `last`), using the specified allocator.

   > -10- Complexity: Makes only N calls to the copy constructor of `T` Initializes exactly N elements (where N is the distance between `first` and `last`) and no reallocations if iterators `first` and `last` are of forward, bidirectional, or random access categories. It makes initializes order N calls to the copy constructor of `T` elements and performs order log N reallocations if they are just input iterators.

   >     template<container-compatible-range<T> R>
   >       constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());

   > -11- Effects: Constructs a `vector` object with the elements of the range `rg`, using the specified allocator.

   > -12- Complexity: Initializes exactly N elements from the results of dereferencing successive iterators of `rg`, where N is `ranges::distance(rg)`. Performs no reallocations if `R` models `ranges::forward_range` or `ranges::sized_range`; otherwise, performs order log N reallocations and initializes order N calls to the copy or move constructor of `T` elements.

6. Modify 23.3.13.3 [vector.capacity] as indicated:

   >     constexpr void reserve(size_type n);

   > -3- Preconditions: `T` is CppMoveInsertable into `vector`.

   > -4- Effects: A directive that informs a `vector` of a planned change in size, so that it can manage the storage allocation accordingly. After `reserve<del>()</del>`, `capacity()` is greater or equal to the argument of `reserve` if reallocation happens; and equal to the previous value of `capacity()` otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of `reserve<del>()</del>`. If an exception is thrown other than by the move constructor of a move-construction of one object of non-Cpp17CopyInsertable type `T` from another, there are no effects.

   > [...]

   >     constexpr shrink_to_fit();

   > -8- Preconditions: `T` is CppMoveInsertable into `vector`.

   > -9- Effects: `shrink_to_fit` is a non-binding request to reduce `capacity()` to `size()`. [Note 2: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] It does not increase `capacity()`, but may reduce `capacity()` by causing reallocation. If an exception is thrown other than by the move constructor move-construction of one object of a non-Cpp17CopyInsertable type `T` from another, there are no effects.

   > [...]

   >     constexpr void resize(size_type sz);

   > -14- Preconditions: `T` is Cpp17MoveInsertable and Cpp17DefaultInsertable into `vector`.

   > -15- Effects: [...]

   > -16- Remarks: If an exception is thrown other than by the move constructor move-construction of one object of a non-Cpp17CopyInsertable type `T` from another, there are no effects.

7. Modify 23.3.13.5 [vector.modifiers] as indicated:

   >     iterator insert(const_iterator position, const T& x);
   >     ...
   >     template<container-compatible-range<T> R>
   >       void append_range(R&& rg);

   > -1- Complexity: [...]

   > -2- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. If no reallocation happens, then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of `T` or by any \tcode{InputIterator} operation the construction or assignment of one object of type `T` from another, or by any `InputIterator` operation, there are no effects. If an exception is thrown while inserting a single element at the end and `T` is Cpp17CopyInsertable or `is_nothrow_move_constructible_v<T>` is `true`, there are no effects. Otherwise, if an exception is thrown by the move constructor of a move-construction of one object of non-Cpp17CopyInsertable type `T` from another, the effects are unspecified.

   >     constexpr iterator erase(const_iterator position);
   >     constexpr iterator erase(const_iterator first, const_iterator last);
   >     constexpr void pop_back();

   > -3- Effects: Invalidates iterators and references at or after the point of the erase.

   > -4- Throws: Nothing unless an exception is thrown by the assignment operator or move assignment operator of `T` construction or assignment of one object of type `T` from another.

   > -5- Complexity: The destructor of `T` is called the number of times number of objects of type `T` destroyed is equal to the number of the elements erased, but thean assignment operator of `T` is called the number of times equal to the number of elements in the vector after the erased elements.

8. Modify 23.3.16.5 [inplace.vector.modifiers] as indicated:

   >     constexpr iterator erase(const_iterator position);
   >     constexpr iterator erase(const_iterator first, const_iterator last);
   >     constexpr void pop_back();

   > -29- Complexity: The destructor of `T` is called the number of times number of objects of type `T` destroyed is equal to the number of the elements erased, but thean assignment operator of `T` is called the number of times equal to the number of elements after the erased elements.

### 4127(i). The Standard Library should not use predicates of the form `pred(*i) != false`

Section: 22.10.18.3 [func.search.bm], 22.10.18.4 [func.search.bmh], 23.2.7.1 [associative.reqmts.general], 23.3.11.5 [list.ops], 26.6.6 [alg.find], 26.6.9 [alg.find.first.of], 26.6.10 [alg.adjacent.find], 26.6.11 [alg.count], 26.6.12 [alg.mismatch], 26.6.15 [alg.search], 26.8.1 [alg.sorting.general] Status: New Submitter: Hewill Kang Opened: 2024-07-25 Last modified: 2024-08-05

Priority: 3

View other active issues in [func.search.bm].

View all other issues in [func.search.bm].

View all issues with New status.

Discussion:

The `boolean-testable` concept introduced in P1964R2 places appropriate constraints on boolean-ish types, so that `!pred(x)` or `i != last && pred(*i)` always has a valid definition.

Subsequently, P2167R3 applied this concept to 26.2 [algorithms.requirements] p6, requiring that `decltype(pred(*first))` should model `boolean-testable`.

However, `boolean-testable` does not guarantee that `pred(*i) != false` is valid, because the type may overload `operator==`. It is necessary to replace this form of expression with an appropriate one as we do in P1964R2.

[2024-07-27; Daniel comments]

I would recommend to add a wrapping "`bool(pred([…]))`" and possibly even extend to "`bool(pred([…]))` is `true`" following our usual wording convention, since an English phrase of the form "if `pred([…])`" where `pred([…])` is potential non-`bool` and the English "if" is not a C++ language `if` (with built-in conversion semantics) doesn't sound like a meaningful phrase to me.

[2024-08-02; Reflector poll]

Set priority to 3 after reflector poll. "Needs more 'is `true`' added". "Would prefer not to have explicit conversions to `bool` unless `boolean-testable` requires that. The 'Let E be' lists don't need an 'is true' there, but the use of 'E' should say 'is true'". [alg.search] and [func.search.bm] have changed editorially in the draft, the proposed resolution needs updating.

Previous resolution [SUPERSEDED]

This wording is relative to N4986.

1. Modify 22.10.18.3 [func.search.bm] as indicated:

   >     boyer_moore_searcher(RandomAccessIterator1 pat_first,
   >                          RandomAccessIterator1 pat_last,
   >                          Hash hf = Hash(),
   >                          BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, the Cpp17CopyConstructible, and the Cpp17CopyAssignable requirements.

   > > -2- Let `V` be `iterator_traits<RandomAccessIterator1>::value_type`. For any two values `A` and `B` of type `V`, if `pred(A, B) == true`, then `hf(A) == hf(B)` is `true`. […]

   > > -7- Returns: A pair of iterators `i` and `j` such that

   > > 1. (7.1) — `i` is the first iterator in the range [`first`, `last - (pat_last_ - pat_first_)`) such that for every non-negative integer `n` less than `pat_last_ - pat_first_` the following condition holds: `pred(*(i + n), *(pat_first_ + n)) != false`, and

   > > 2. […]

2. Modify 22.10.18.4 [func.search.bmh] as indicated:

   >     boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first,
   >                                   RandomAccessIterator1 pat_last,
   >                                   Hash hf = Hash(),
   >                                   BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.

   > > -2- Let `V` be `iterator_traits<RandomAccessIterator1>::value_type`. For any two values `A` and `B` of type `V`, if `pred(A, B) == true`, then `hf(A) == hf(B)` is `true`. […]

   > > -7- Returns: A pair of iterators `i` and `j` such that

   > > 1. (7.1) — `i` is the first iterator in the range [`first`, `last - (pat_last_ - pat_first_)`) such that for every non-negative integer `n` less than `pat_last_ - pat_first_` the following condition holds: `pred(*(i + n), *(pat_first_ + n)) != false`, and

   > > 2. […]

3. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   > -3- The phrase "equivalence of keys" means the equivalence relation imposed by the comparison object. That is, two keys `k1` and `k2` are considered to be equivalent if for the comparison object `comp`, `!comp(k1, k2) == false && !comp(k2, k1) == false`.

   > […]

   > -177- The fundamental property of iterators of associative containers is that they iterate through the containers in the non-descending order of keys where non-descending is defined by the comparison that was used to construct them. For any two dereferenceable iterators `i` and `j` such that distance from `i` to `j` is positive, the following condition holds:

   >     !value_comp(*j, *i) == false

   > -178- For associative containers with unique keys the stronger condition holds:

   >     value_comp(*i, *j) != false

4. Modify 23.3.11.5 [list.ops] as indicated:

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > -15- Effects: Erases all the elements in the list referred to by a list iterator `i` for which the following conditions hold: `*i == value, pred(*i) != false`. Invalidates only the iterators and references to the erased elements.

   > > -16- Returns: The number of elements erased.

   > > -17- Throws: Nothing unless an exception is thrown by `*i == value` or `pred(*i) != false`.

   > > […]

5. Modify 26.6.6 [alg.find] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == value` for `find`;

   > 2. (1.2) — `pred(*i) != false` for `find_if`;

   > 3. (1.3) — `!pred(*i) == false` for `find_if_not`;

   >    […]

6. Modify 26.6.9 [alg.find.first.of] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == *j` for the overloads with no parameter `pred`;

   > 2. (1.2) — `pred(*i, *j) != false` for the overloads with a parameter `pred` and no parameter `proj1`;

   >    […]

7. Modify 26.6.10 [alg.adjacent.find] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == *(i + 1)` for the overloads with no parameter `pred`;

   > 2. (1.2) — `pred(*i, *(i + 1)) != false` for the overloads with a parameter `pred` and no parameter `proj1`;

   >    […]

8. Modify 26.6.11 [alg.count] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == value` for the overloads with no parameter `pred` or `proj`;

   > 2. (1.2) — `pred(*i) != false` for the overloads with a parameter `pred` but no parameter `proj`;

   >    […]

9. Modify 26.6.12 [alg.mismatch] as indicated:

   > -2- Let E be:

   > 1. (2.1) — `!(*(first1 + n) == *(first2 + n))` for the overloads with no parameter `pred`;

   > 2. (2.2) — `!pred(*(first1 + n), *(first2 + n)) == false` for the overloads with a parameter `pred` and no parameter `proj1`;

   >    […]

10. Modify 26.6.15 [alg.search] as indicated:

    > -1- Returns: The first iterator `i` in the range [`first1`, `last1 - (last2-first2)`) such that for every non-negative integer `n` less than `last2 - first2` the following corresponding conditions hold: `*(i + n) == *(first2 + n), pred(*(i + n), *(first2 + n)) != false`. Returns `first1` if [`first2`, `last2`) is empty, otherwise returns `last1` if no such iterator is found.

    > […]

    > -6- Returns: The first iterator `i` in the range [`first`, `last-count`) such that for every non-negative integer `n` less than `count` the following corresponding conditions hold: `*(i + n) == value, pred(*(i + n), value) != false`. Returns `last` if no such iterator is found.

11. Modify 26.8.1 [alg.sorting.general] as indicated:

    > -3- For all algorithms that take `Compare`, there is a version that uses `operator<` instead. That is, `comp(*i, *j) != false` defaults to `*i < *j != false`. For algorithms other than those described in 26.8.4 [alg.binary.search], `comp` shall induce a strict weak ordering on the values.

[2024-08-03; Daniel provides improved wording]

The current wording is inconsistent in regard to explicit conversion to `bool` and lack of them in cases of expressions whose value is required to satisfy the `boolean-testable` constraints. To realize consistent results for all subclause references touched by the changes required by this issue I decided that every E definition remains unconverted but and that every E evaluation is interpreted as if an implied `bool` conversion has been applied based on the reflector preference for that simplified approach.

Nonetheless, during the wording preparation I noticed that the wording in the Throws: element 23.3.11.5 [list.ops] p17 is seriously missing the additional extra conversion to `bool` for both expressions, because the `boolean-testable` requirements do not impose a no-throw requirement for that conversion, and they must therefore be included here.

This problem will be handled by a separate issue (LWG 4132(i)), because the rationale for this change is different from the actual target of this issue and not related to the other consistency adjustments done by the wording below.

Proposed resolution:

This wording is relative to N4988.

1. Modify 22.10.18.3 [func.search.bm] as indicated:

   >     boyer_moore_searcher(RandomAccessIterator1 pat_first,
   >                          RandomAccessIterator1 pat_last,
   >                          Hash hf = Hash(),
   >                          BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.

   > > -2- Let `V` be `iterator_traits<RandomAccessIterator1>::value_type`. For any two values `A` and `B` of type `V`, if `pred(A, B) == true` is `true`, then `hf(A) == hf(B)` is `true`. […]

   >     template<class RandomAccessIterator2>
   >       pair<RandomAccessIterator2, RandomAccessIterator2>
   >         operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > > […]

   > > -7- Returns: A pair of iterators `i` and `j` such that

   > > 1. (7.1) — `i` is the first iterator in the range [`first`, `last - (pat_last_ - pat_first_)`) such that for every non-negative integer `n` less than `pat_last_ - pat_first_` the following condition holds: `pred(*(i + n), *(pat_first_ + n)) != false` is `true`, and

   > > 2. (7.2) — `j == next(i, distance(pat_first_, pat_last_))` is `true`.

2. Modify 22.10.18.4 [func.search.bmh] as indicated:

   >     boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first,
   >                                   RandomAccessIterator1 pat_last,
   >                                   Hash hf = Hash(),
   >                                   BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.

   > > -2- Let `V` be `iterator_traits<RandomAccessIterator1>::value_type`. For any two values `A` and `B` of type `V`, if `pred(A, B) == is true`, then `hf(A) == hf(B)` is `true`. […]

   >     template<class RandomAccessIterator2>
   >       pair<RandomAccessIterator2, RandomAccessIterator2>
   >         operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > > […]

   > > -7- Returns: A pair of iterators `i` and `j` such that

   > > 1. (7.1) — `i` is the first iterator in the range [`first`, `last - (pat_last_ - pat_first_)`) such that for every non-negative integer `n` less than `pat_last_ - pat_first_` the following condition holds: `pred(*(i + n), *(pat_first_ + n)) != false` is `true`, and

   > > 2. (7.2) — `j == next(i, distance(pat_first_, pat_last_))` is `true`.

3. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   > -3- The phrase "equivalence of keys" means the equivalence relation imposed by the comparison object. That is, two keys `k1` and `k2` are considered to be equivalent if for the comparison object `comp`, `!comp(k1, k2) == false && !comp(k2, k1) == false` is `true`.

   > […]

   > -177- The fundamental property of iterators of associative containers is that they iterate through the containers in the non-descending order of keys where non-descending is defined by the comparison that was used to construct them. For any two dereferenceable iterators `i` and `j` such that distance from `i` to `j` is positive, the following condition holds:

   > > `!value_comp(*j, *i) == false` is `true`.

   > -178- For associative containers with unique keys the stronger condition holds:

   > > `value_comp(*i, *j) != false` is `true`.

4. Modify 23.3.11.5 [list.ops] as indicated:

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > -15- Effects: Erases all the elements in the list referred to by a list iterator `i` for which the following conditions hold: `*i == value` is `true`, `pred(*i) != false` is `true`. Invalidates only the iterators and references to the erased elements.

   > > -16- Returns: The number of elements erased.

   > > -17- Throws: Nothing unless an exception is thrown by `*i == value` or `pred(*i) != false`.

   > > […]

5. Modify 26.6.6 [alg.find] as indicated:

   > [Drafting note: Sub-bullets (1.4), (1.5), and (1.6) below regarding `invoke` expressions are similarly required model `boolean-testable`, as specified by concept `predicate` (18.7.4 [concept.predicate]), therefore the explicit conversion is removed here for consistency]

   > -1- Let E be:

   > 1. (1.1) — `*i == value` for `find`;

   > 2. (1.2) — `pred(*i) != false` for `find_if`;

   > 3. (1.3) — `!pred(*i) == false` for `find_if_not`;

   > 4. (1.4) — `bool(invoke(proj, *i) == value)` for `ranges::find`;

   > 5. (1.5) — `bool(invoke(pred, invoke(proj, *i)))` for `ranges::find_if`;

   > 6. (1.6) — `bool(!invoke(pred, invoke(proj, *i)))` for `ranges::find_if_not`.

   > -2- Returns: The first iterator `i` in the range `[first, last)` for which E is `true`. Returns `last` if no such iterator is found.

6. Modify 26.6.9 [alg.find.first.of] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == *j` for the overloads with no parameter `pred`;

   > 2. (1.2) — `pred(*i, *j) != false` for the overloads with a parameter `pred` and no parameter `proj1`;

   > 3. (1.3) — `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))` for the overloads with parameters `pred` and `proj1`.

   > […]

   > -3- Returns: The first iterator `i` in the range `[first1, last1)` such that for some iterator `j` in the range `[first2, last2)` E holds is `true`. Returns `last1` if `[first2, last2)` is empty or if no such iterator is found.

7. Modify 26.6.10 [alg.adjacent.find] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == *(i + 1)` for the overloads with no parameter `pred`;

   > 2. (1.2) — `pred(*i, *(i + 1)) != false` for the overloads with a parameter `pred` and no parameter `proj`;

   > 3. (1.3) — `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))` for the overloads with both parameters `pred` and `proj`.

   > -2- Returns: The first iterator `i` such that both `i` and `i + 1` are in the range `[first, last)` for which E holds is `true`. Returns `last` if no such iterator is found.

8. Modify 26.6.11 [alg.count] as indicated:

   > -1- Let E be:

   > 1. (1.1) — `*i == value` for the overloads with no parameter `pred` or `proj`;

   > 2. (1.2) — `pred(*i) != false` for the overloads with a parameter `pred` but no parameter `proj`;

   > 3. (1.3) — `invoke(proj, *i) == value` for the overloads with a parameter `proj` but no parameter `pred`;

   > 4. (1.4) — `bool(invoke(pred, invoke(proj, *i)))` for the overloads with both parameters `proj` and `pred`.

   > -2- Effects: Returns the number of iterators `i` in the range `[first, last)` for which E holds is `true`.

9. Modify 26.6.12 [alg.mismatch] as indicated:

   > -2- Let E be:

   > 1. (2.1) — `!(*(first1 + n) == *(first2 + n))` for the overloads with no parameter `pred`;

   > 2. (2.2) — `!pred(*(first1 + n), *(first2 + n)) == false` for the overloads with a parameter `pred` and no parameter `proj1`;

   > 3. (2.3) — `!invoke(pred, invoke(proj1, *(first1 + n)), invoke(proj2, *(first2 + n)))` for the overloads with both parameters `pred` and `proj1`.

   > […]

   > -4- Returns: `{ first1 + n, first2 + n }`, where `n` is the smallest integer in [`0`, N) such that E holds is `true`, or N if no such integer exists.

10. Modify 26.6.15 [alg.search] as indicated:

    > -1- Returns: The first iterator `i` in the range [`first1`, `last1 - (last2-first2)`) such that for every non-negative integer `n` less than `last2 - first2` the following corresponding conditions hold: `*(i + n) == *(first2 + n)` is `true`, `pred(*(i + n), *(first2 + n)) != false` is `true`. Returns `first1` if [`first2`, `last2`) is empty, otherwise returns `last1` if no such iterator is found.

    > […]

    > -6- Let E be `pred(*(i + n), value) != false` for the overloads with a parameter `pred`, and `*(i + n) == value` otherwise.

    > -7- Returns: The first iterator `i` in the range `[first, last - count)` such that for every non-negative integer `n` less than `count` the condition E is `true`. Returns `last` if no such iterator is found.

11. Modify 26.8.1 [alg.sorting.general] as indicated:

    > [Drafting note: The wording below does not require extra "is `true`" added to the adjusted expressions. This is comparable to the E cases above, since 26.8.1 [alg.sorting.general] p2 already points out:

    > > The return value of the function call operation applied to an object of type `Compare`, when converted to `bool`, yields `true` if the first argument of the call is less than the second, and `false` otherwise.

    > ]

    > -3- For all algorithms that take `Compare`, there is a version that uses `operator<` instead. That is, `comp(*i, *j) != false` defaults to `*i < *j != false`. For algorithms other than those described in 26.8.4 [alg.binary.search], `comp` shall induce a strict weak ordering on the values.

### 4128(i). Allocator requirements should not allow rebinding conversions to be explicit

Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jonathan Wakely Opened: 2024-08-02 Last modified: 2024-08-21

Priority: 3

View other active issues in [allocator.requirements.general].

View all other issues in [allocator.requirements.general].

View all issues with New status.

Discussion:

The Cpp17Allocator requirements require a non-explicit copy constructor, but do not require a non-explicit converting constructor used for rebinding.

Since C++14 it has been clearly stated that "An allocator type `X` shall satisfy the requirements of CopyConstructible". That requires `X u = a;` to work as well as `X u(a);`, but only when the type of `a` is `X`. Constructing a rebound allocator from another specialization of the same allocator class template is only required to work using direct-initialization, `X u(b);`. This means it's permitted to make the converting constructor explicit, so that `X u = b;` is ill-formed. There seems to be no good reason to allow allocators to make that ill-formed.

In fact, there seems to be a good reason to not allow it. The `uses_allocator` trait is defined in terms of `is_convertible`, not `is_constructible`, which means that if `Alloc<T>` has an explicit converting constructor, then `uses_allocator_v<X, Alloc<T>>` will be false for a type with `X::allocator_type = Alloc<U>`, because you would need to explicitly convert an `Alloc<T>` to `Alloc<U>` before passing it to `X`'s constructor.

That is at least consistent: the trait gives the right answer even for allocators with explicit conversions. It doesn't seem very useful though, and if users don't carefully check `uses_allocator` with exactly the right types they might get errors unless they carefully rebind and convert their allocators explicitly. Or worse, if they rely on other library components to check `uses_allocator`, they might silently get the wrong behaviour. For example, trying to construct an `X` with an `Alloc<T>` (e.g. via `make_obj_using_allocator`) could silently fail to pass the allocator to the constructor because there's no implicit conversion to `X::allocator_type`, even though you're providing an allocator of the right "family" and trying to use it. So a constructor without an allocator argument could be chosen when you thought you were supplying an allocator for the object to use.

There seemed to be consensus when LWG discussed it that we should just require conversions to be implicit, so that allocators are not silently ignored because they cannot be implicitly converted.

During the discussion it was noted that assigning allocators is only needed when the propagation trait is true, but such assignments are always done between objects of the same allocator type. So the allocator requirements do not need to require converting assignments to work.

[2024-08-21; Reflector poll]

Set priority to 3 after reflector poll. Jonathan to add an Annex C entry about the change.

Proposed resolution:

This wording is relative to N4986.

1. Modify 16.4.4.6.1 [allocator.requirements.general] as indicated:

   >     X u(a);
   >     X u = a;

   > -63- Postconditions: `u == a` is `true`.

   > -64- Throws: Nothing.

   >     X u(b);
   >     X u = b;

   > -65- Postconditions: `Y(u) == b` and `u == X(b)` are both `true`.

   > -66- Throws: Nothing.

   >     X u(std::move(a));
   >     X u = std::move(a);

   > -67- Postconditions: The value of `a` is unchanged and is equal to `u`.

   > -68- Throws: Nothing.

   >     X u(std::move(b));
   >     X u = std::move(b);

   > -69- Postconditions: `u` is equal to the prior value of `X(b)`.

   > -70- Throws: Nothing.

### 4129(i). Possibly incorrect wording for data race avoidance

Section: 16.4.6.10 [res.on.data.races] Status: New Submitter: Jiang An Opened: 2024-07-30 Last modified: 2025-10-14

Priority: 3

View other active issues in [res.on.data.races].

View all other issues in [res.on.data.races].

View all issues with New status.

Discussion:

From PR cplusplus/draft#6748 which was closed as non-editorial.

Currently, 16.4.6.10 [res.on.data.races] is talking about arguments and "including `this`", but `this` is not a function argument. Moreover, it seems more appropriate to say that a function accesses some object via a parameter.

It might need to be considered whether we should use the more general term "range" instead of "container", which will cover `std::span`, etc.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

"'non-const parameters' needs to be fixed, this isn't about top-level const, e.g. `std::string&` is not a const type."

Proposed resolution:

This wording is relative to N4986.

1. Modify 16.4.6.10 [res.on.data.races] as indicated:

   > -2- A C++ standard library function shall not directly or indirectly access objects (6.10.2 [intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's argumentsparameters, including `this` the object parameter (if any).

   > -3- A C++ standard library function shall not directly or indirectly modify objects (6.10.2 [intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's non-const argumentsparameters, including `this` the object parameter (if any).

   > […]

   > -5- A C++ standard library function shall not access objects indirectly accessible via its argumentsparameters or via elements of its container argumentsparameters except by invoking functions required by its specification on those container elements.

### 4132(i). Throws specifications need to include `boolean-testable` operations

Section: 23.2.7.1 [associative.reqmts.general], 23.3.11.5 [list.ops], 23.3.7.6 [forward.list.ops] Status: New Submitter: Daniel Krügler Opened: 2024-08-03 Last modified: 2024-10-02

Priority: 3

View other active issues in [associative.reqmts.general].

View all other issues in [associative.reqmts.general].

View all issues with New status.

Discussion:

The standard provides various Throws: elements whose specification refers to boolean-like expressions, for example 23.3.11.5 [list.ops] says:

> -17- Throws: Nothing unless an exception is thrown by `*i == value` or `pred(*i) != false`.

The expressions `*i == value` (by [tab:cpp17.equalitycomparable]) and `pred(*i)` (by 26.2 [algorithms.requirements]) both require to have a type that models `boolean-testable`, so their actual evaluation potentially requires an additional conversion to `bool` (The part "`!= false`" was not guaranteed to realize that as pointed out by LWG 4127(i) and will therefore be removed by that issue).

The `boolean-testable` requirements (18.5.2 [concept.booleantestable]) doesn't say anything about potential exceptions thrown by this conversion, so we must assume that this conversion allows an exception to be thrown.

An additional problem arises by the fact that the `boolean-testable` requirements do not require that the implicit and explicit conversion to `bool` have to behave consistently in regard to exceptions. 18.4.4 [concept.convertible] only says that "The implicit and explicit conversions are required to produce equal results". But since we don't want to restrict implementations to a specific `bool` conversion, the Throws wording should include it in a manner that applies to any form.

Besides the obviously necessary conversion to `bool` we should not restrict implementations in regard to the potential usage of other `boolean-testable` operations, e.g. negation, which could potentially also throw an exception.

Therefore the wording for the affected Throws: elements somehow needs to mention `boolean-testable` operations to cover the effective operation that is involved here without actually restricting existing implementations unnecessarily.
Previous resolution [SUPERSEDED]

This wording is relative to N4986.

1. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   >     a.merge(a2)

   > > […]

   > > -116- Throws: Nothing unless the evaluation of the comparison object including every selected `boolean-testable` (18.5.2 [concept.booleantestable]) operation throws.

2. Modify 23.3.7.6 [forward.list.ops] as indicated:

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > […]

   > > -15- Throws: Nothing unless an exception is thrown by the equality comparison or the predicate evaluation including every selected `boolean-testable` (18.5.2 [concept.booleantestable]) operation.

   > […]

   >     size_type unique();
   >     template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);

   > > […]

   > > -22- Throws: Nothing unless an exception is thrown by the predicate evaluation including every selected `boolean-testable` (18.5.2 [concept.booleantestable]) operation.

3. Modify 23.3.11.5 [list.ops] as indicated:

   > [Drafting note: The adjustment of p17 is presented as if issue LWG 4127(i) proposed wording has already been applied]

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > […]

   > > -17- Throws: Nothing unless an exception is thrown by `*i == value` or `pred(*i)` including every selected `boolean-testable` (18.5.2 [concept.booleantestable]) operation.

   > > […]

   >     size_type unique();
   >     template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);

   > > […]

   > > -24- Throws: Nothing unless an exception is thrown by the predicate evaluation including every selected `boolean-testable` (18.5.2 [concept.booleantestable]) operation.

   > > […]

[2024-09-07; Daniel comments and improves wording]

During LWG reflector discussion it has been proposed to add similar wording to the throwing prohibition in Cpp17NullablePointer and `nothrow-sentinel-for`, too. Furthermore, the existing wording changes are becoming now more harmonized in their stylistic forms.

[2024-10-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4988.

1. Modify 16.4.4.4 [nullablepointer.requirements] as indicated:

   > -4- No operation which is part of the Cpp17NullablePointer requirements, including each of its `boolean-testable` (18.5.2 [concept.booleantestable]) operations, shall exit via an exception.

2. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   >     a.merge(a2)

   > > […]

   > > -116- Throws: Nothing unless an exception is thrown by evaluation of the comparison object, including any selected `boolean-testable` (18.5.2 [concept.booleantestable]) operationsthrows.

3. Modify 23.3.7.6 [forward.list.ops] as indicated:

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > […]

   > > -15- Throws: Nothing unless an exception is thrown by the equality comparison (for `remove()`) or the predicate (for `remove_if()`), including any selected `boolean-testable` (18.5.2 [concept.booleantestable]) operations.

   > […]

   >     size_type unique();
   >     template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);

   > > […]

   > > -22- Throws: Nothing unless an exception is thrown by evaluation of the predicate, including any selected `boolean-testable` (18.5.2 [concept.booleantestable]) operations.

4. Modify 23.3.11.5 [list.ops] as indicated:

   > [Drafting note: The adjustment of p17 is presented as if issue LWG 4127(i) proposed wording has already been applied]

   >     size_type remove(const T& value);
   >     template<class Predicate> size_type remove_if(Predicate pred);

   > > […]

   > > -17- Throws: Nothing unless an exception is thrown by `*i == value` or `pred(*i)`the equality comparison (for `remove()`) or evaluation of the predicate (for `remove_if()`), including any selected `boolean-testable` (18.5.2 [concept.booleantestable]) operations.

   > > […]

   >     size_type unique();
   >     template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);

   > > […]

   > > -24- Throws: Nothing unless an exception is thrown by evaluation of the predicate, including any selected `boolean-testable` (18.5.2 [concept.booleantestable]) operations.

   > > […]

5. Modify 26.11.2 [special.mem.concepts] as indicated:

   >     template<class S, class I>
   >     concept nothrow-sentinel-for = sentinel_for<S, I>; // exposition only

   > > -4- Types `S` and `I` model `nothrow-sentinel-for` only if no exceptions are thrown from copy construction, move construction, copy assignment, move assignment, or comparisons , including each of its `boolean-testable` (18.5.2 [concept.booleantestable]) operations, between valid values of type `I` and `S`.

   > > -5- [Note 2: This concept allows some `sentinel_for` (24.3.4.7 [iterator.concept.sentinel]) operations to throw exceptions. — end note]

### 4138(i). `is_within_lifetime` should mandate `is_object`

Section: 21.3.11 [meta.const.eval] Status: New Submitter: Corentin Opened: 2024-08-09 Last modified: 2026-06-10

Priority: 3

View all issues with New status.

Discussion:

>     int f();
>     std::is_within_lifetime<int()>(f);

This is currently well-formed, and only fails when evaluated because `is_within_lifetime` is not constrained. However talking of lifetime of a non-object does not make sense, and the lack of constraint makes the implementation and use of that function and underlying built-in more convoluted than necessary.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

"What about `void*`, should that be allowed? The compiler should know which union member the `void*` came from, as this is only using during constant evaluation."

"NAD, a function could have its lifetime constrained by a dynamic loader, so it seems wrong to assume that taling of function lifetimes makes no sense."

Previous resolution [SUPERSEDED]

This wording is relative to N4988.

1. Modify 21.3.3 [meta.type.synop], header `<type_traits>` synopsis, as indicated:

   >     […]
   >     // 21.3.11 [meta.const.eval], constant evaluation context
   >     constexpr bool is_constant_evaluated() noexcept;
   >     template<class T>
   >     consteval bool is_within_lifetime(const Tauto*) noexcept;

2. Modify 21.3.11 [meta.const.eval] as indicated:

   >     template<class T>
   >     consteval bool is_within_lifetime(const Tauto* p) noexcept;

   > > -?- Mandates: `is_object_v<T>` is `true`.

   > > -3- Returns: `true` if `p` is a pointer to an object that is within its lifetime (6.8.4 [basic.life]); otherwise, `false`.

   > > -4- Remarks: During the evaluation of an expression `E` as a core constant expression, a call to this function is ill-formed unless `p` points to an object that is usable in constant expressions or whose complete object's lifetime began within `E`.

[2026-06-09; Tim rebases and provides new wording]

Functions do not have lifetimes as far as the standard is concerned. However, there is no reason to ban `void`.

Proposed resolution:

This wording is relative to N5046.

1. Modify 21.3.11 [meta.const.eval] as indicated:

   >     template<class U = void, class T>
   >       consteval bool is_within_lifetime(const T* p) noexcept;

   > > -3- Mandates: `static_cast<const volatile U*>(p)` is well-formed. `T` is an object type or cv `void`.

   > > -4- Returns: `true` if `p` is a pointer to an object that is within its lifetime (6.8.4 [basic.life]) and `static_cast<const volatile U*>(p)` is a constant subexpression; otherwise, `false`.

   > > -4- Remarks: During the evaluation of an expression `E` as a core constant expression, a call to this function is ill-formed unless `p` points to an object that is usable in constant expressions or whose complete object's lifetime began within `E`.

### 4139(i). §[time.zone.leap] recursive constraint in `<=>`

Section: 30.11.8 [time.zone.leap] Status: New Submitter: Corentin Opened: 2024-08-18 Last modified: 2024-08-21

Priority: 3

View all other issues in [time.zone.leap].

View all issues with New status.

Discussion:

Consider

>     decltype(std::declval<std::chrono::leap_second&>() <=> std::chrono::system_clock::now())

There is a `<=>` operator for leap second defined as

>     template<class Duration>
>       requires three_way_comparable_with<sys_seconds, sys_time<Duration>>
>       constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y) noexcept;

In order to resolve this overload, we need to check the constraints. `three_way_comparable_with` will end up checking that `sys_seconds{} < sys_time<Duration>{}` is a valid expression. To do that, we run overload resolution, find a bunch of `operator<=>`, including the `leap_second` overload mentioned above. We check its constraints... and we find ourselves doing that recursively.

This problem currently doesn't manifest in production compilers because of a number of non-conforming behaviors of all implementations, but surfaced while fixing some of these issues in clang.

libstdc++ also does not suffer from this issue because `leap_seconds` comparisons operators are all hidden friends (in fact, libstdc++ define hidden friends comparison operators for most of the objects in chrono, which is nice!)

Suggested resolution:

Specify that `leap_seconds` operators are hidden friends. This would avoid the recursion, and would be easier on compilers.

[2024-08-21; Reflector poll]

Set priority to 3 after reflector poll. Support for changing all relational ops for calendar types to hidden friends, but only doing it for `leap_second` would be in scope for this issue.

Proposed resolution:

### 4145(i). Unclear how [res.on.data.races] apply to templated functions

Section: 16.4.6.10 [res.on.data.races] Status: New Submitter: Tim Song Opened: 2024-08-22 Last modified: 2024-10-02

Priority: 3

View other active issues in [res.on.data.races].

View all other issues in [res.on.data.races].

View all issues with New status.

Discussion:

It's unclear how 16.4.6.10 [res.on.data.races] applies to templated functions that invoke user-supplied operations. In particular, there's no way to meet those requirements when a user-supplied operation can perform arbitrary operations.

Perhaps we need to exclude the invocation of such operations from the scope of the requirements.

[2024-10-02; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4152(i). The primary template of `std::char_traits` is totally underspecified

Section: 27.2.2 [char.traits.require] Status: New Submitter: Jiang An Opened: 2024-09-08 Last modified: 2025-02-07

Priority: 4

View other active issues in [char.traits.require].

View all other issues in [char.traits.require].

View all issues with New status.

Discussion:

Currently, only explicit specializations of `std::char_traits` are specified in the standard. Nothing is specified for the primary template except that it's not marked "not defined".

Given some implementations provide the definition of the primary template (perhaps as an extension), it might be better to explicitly say that instantiation of the primary template has unspecified effect. Also, it seems outdated to say "explicit specializations" as partial specializations are possible since C++20.

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll.

27.2.1 [char.traits.general]/1 says that the subclause "defines a class template `char_traits<charT>`", but we never provided a definition.

"Libc++ started to warn that the primary template is deprecated and will be removed. The proposed resolution doesn't improve matters though."

"The definition should be cross-platform. Unspecified doesn't help."

Proposed resolution:

This wording is relative to N4988.

1. Modify 27.2.2 [char.traits.require] as indicated:

   > -2- The class template

   > >     template<class charT> struct char_traits;

   > is provided in the header `<string>` as a basis for explicit and partial specializations. The effect of instantiating the primary template of `char_traits` is unspecified and possibly makes the program ill-formed.

### 4155(i). Cpp17NullablePointer should require that some expression can be contextually converted to bool

Section: 16.4.4.4 [nullablepointer.requirements] Status: New Submitter: Jiang An Opened: 2024-09-18 Last modified: 2024-10-02

Priority: 3

View all other issues in [nullablepointer.requirements].

View all issues with New status.

Discussion:

Currently, 16.4.4.4 [nullablepointer.requirements]/3 requires "An object `p` of type `P` can be contextually converted to `bool`", while the core language wording (7.3.1 [conv.general]/4) only says some expressions can be contextually converted to `bool`. Moreover, the value category and cv-qualification are not mentioned, presumably both non-const and const `P`, together with all value categories are expected.

[2024-10-02; Reflector poll]

Set priority to 3 after reflector poll.

"This is an improvement, but judging from microsoft/STL 4964, I think we actually want to define a "contextually-boolean-testable" for this."

Proposed resolution:

This wording is relative to N4988.

1. Modify 16.4.4.4 [nullablepointer.requirements] as indicated:

   > -3- An object `p` of type `P` can be contextually converted to `bool`. The effect shall be as if `p != nullptr` had been evaluated in place of `p`.

2. Add a row to the bottom of [tab:cpp17.nullablepointer]:

   > Expression | Return type | Operational semantics
   > --- | --- | ---
   > ... | ... | ...
   > `a ? true : false` | `bool` | `a != np`

### 4156(i). `error_category` messages have unspecified encoding

Section: 19.5.3.2 [syserr.errcat.virtuals] Status: SG16 Submitter: Victor Zverovich Opened: 2024-09-18 Last modified: 2025-03-12

Priority: 3

View all issues with SG16 status.

Discussion:

19.5.3.1 [syserr.errcat.overview] says:

> The class `error_category` serves as a base class for types used to identify the source and encoding of a particular category of error code.

However, this doesn't seem to be referring to a character encoding, just something about how an error is encoded into an integer value. The definition of `error_category::message` (19.5.3.2 [syserr.errcat.virtuals] p5) just says:

>     virtual string message(int ev) const = 0;

> Returns: A string that describes the error condition denoted by `ev`.

This says nothing about character encoding either.

There is also implementation divergence: some implementations use variants of `strerror` which return messages in the current C locale encoding, but at least one major implementation doesn't use the current C locale: MSVC STL issue 4711.

Using the current C locale is obviously problematic. First, it is inconsistent with other C++ APIs that normally use C++ locales. Second, because it is a global state, it may change (possibly from another thread) between the time the message is obtained and the time it needs to be consumed, which may lead to mojibake. At the very least there should be a mechanism that captures the encoding information in a race-free manner and communicates it to the caller if the locale encoding is used although it is better not to use it in the first place.

This is somewhat related to LWG 4087(i) but should probably be addressed first because it may affect how some exceptions are defined.

The proposed resolution is similar to the one of LWG 4087(i).

[2024-09-18; Jonathan comments]

It might make sense to stop using the word "encoding" in 19.5.3.1 [syserr.errcat.overview].

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"Do we need to say something about `name()` too? Does this requirement apply to user overrides? If it does, what's the consequence of a violation? UB? This 'encoding for strings returned by the library' questions feels like it should be comprehensively addressed in a paper rather than as a patchwork of individual issues."

[2025-03-12; update from SG16]

Would be resolved by P3395R1.

Proposed resolution:

This wording is relative to N4988.

1. Modify 19.5.3.2 [syserr.errcat.virtuals] as indicated:

   >     virtual string message(int ev) const = 0;

   > -5- Returns: A string in the ordinary literal encoding that describes the error condition denoted by `ev`.

### 4159(i). Uses-allocator construction mechanisms should be opted out for node handles

Section: 23.2.5 [container.node], 20.2.8.1 [allocator.uses.trait] Status: New Submitter: Jiang An Opened: 2024-09-21 Last modified: 2024-10-09

Priority: 3

View all issues with New status.

Discussion:

Currently, node handle types (23.2.5 [container.node]) have the member `allocator_type` type alias, which makes `std::uses_allocator` report `true` for them. However, they don't have allocator-extended constructors, and it's unclear to me whether it's better to make them have such constructors.

The status quo renders the following seemingly reasonable program ill-formed.

>     #include <set>
>     #include <map>

>     int main() {
>       std::pmr::set<int> s{1};
>       std::pmr::map<int, decltype(s)::node_type> m;
>       m.emplace(1, s.extract(1));
>     }

Perhaps we should consistently treat node handles not uses-allocator-constructible.

[2024-10-09; Reflector poll]

Set priority to 3 after reflector poll.

"Strike the carve outs for program-defined specializations." If it's broken for `uses_allocator<NH, A>` to be true when `NH` is not a program-defined type, then we don't want to allow users to make it true for a program-defined `A`, that would still be broken.

Previous resolution [SUPERSEDED]

This wording is relative to N4988.

1. Modify 23.2.5.1 [container.node.overview] as indicated:

   > […]

   > -4- If a user-defined specialization of `pair` exists for `pair<const Key, T>` or `pair<Key, T>`, where `Key` is the container's `key_type` and `T` is the container's `mapped_type`, the behavior of operations involving node handles is undefined.

   > -?- For each node handle type `NH` and any type `A`, if `uses_allocator<NH, A>` is not a program-defined specialization, it meets the Cpp17BinaryTypeTrait requirements (21.3.2 [meta.rqmts]) and its base characteristic is `false_type`.

[2024-10-09; Adjust wording as requested in reflector poll]

Proposed resolution:

This wording is relative to N4988.

1. Modify 23.2.5.1 [container.node.overview] as indicated:

   > […]

   > -4- If a user-defined specialization of `pair` exists for `pair<const Key, T>` or `pair<Key, T>`, where `Key` is the container's `key_type` and `T` is the container's `mapped_type`, the behavior of operations involving node handles is undefined.

   > -?- For each node handle type `NH` and any type `A`, `uses_allocator<NH, A>` meets the Cpp17BinaryTypeTrait requirements (21.3.2 [meta.rqmts]) and its base characteristic is `false_type`.

### 4160(i). `packaged_task` should reject rvalue reference return types

Section: 32.10.10.1 [futures.task.general] Status: New Submitter: Casey Carter Opened: 2024-09-28 Last modified: 2025-02-07

Priority: 3

View all issues with New status.

Discussion:

`promise`, `future`, and `shared_future` all refuse rvalue reference types as template arguments (e.g., 32.10.6 [futures.promise] paragraphs 1 and 2), but `packaged_task<meow&&()>` violates no requirements. Its member `get_future` returns `future<meow&&>`, which is ill-formed, but the other member functions appear to be callable. Nonetheless, at least MSVCSTL, libc++, and libstdc++ all fail to compile simple uses of `packaged_task` with a function type that has an rvalue reference return type (see https://www.godbolt.org/z/5E18nn896).

Presumably the design intent — which the implementers all inferred — is that `packaged_task` should be ill-formed when `get_future` is not instantiable. The spec should say so explicitly rather than relying on the fact that one of the basis operations is unusable.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"Shouldn't it be ill-formed only if instantiated?"

Previous resolution [SUPERSEDED]

This wording is relative to N4988.

1. Modify 32.10.10.1 [futures.task.general] as indicated:

   > […]

   > -2- When the `packaged_task` object is invoked, its stored task is invoked and the result (whether normal or exceptional) stored in the shared state. Any futures that share the shared state will then be able to access the stored result.

   > >     namespace std {
   > >       template<class> class packaged_task; // not defined

   > >       template<class R, class... ArgTypes>
   > >       class packaged_task<R(ArgTypes...)> {
   > >         […]
   > >       };

   > >       template<class R, class... ArgTypes>
   > >       packaged_task(R (*)(ArgTypes...)) -> packaged_task<R(ArgTypes...)>;

   > >       template<class F> packaged_task(F) -> packaged_task<see below>;
   > >     }

   > -?- The program is ill-formed if `R` is an rvalue reference type.

[2025-02-07; Jonathan provides improved wording]

Proposed resolution:

This wording is relative to N5001.

1. Modify 32.10.10.1 [futures.task.general] as indicated:

   > […]

   > -2- When the `packaged_task` object is invoked, its stored task is invoked and the result (whether normal or exceptional) stored in the shared state. Any futures that share the shared state will then be able to access the stored result.

   > >     namespace std {
   > >       template<class> class packaged_task; // not defined

   > >       template<class R, class... ArgTypes>
   > >       class packaged_task<R(ArgTypes...)> {
   > >         […]
   > >       };

   > >       template<class R, class... ArgTypes>
   > >       packaged_task(R (*)(ArgTypes...)) -> packaged_task<R(ArgTypes...)>;

   > >       template<class F> packaged_task(F) -> packaged_task<see below>;
   > >     }

   > -?- If the definition of a specialization `packaged_task<R(Args...)>` is instantiated for an rvalue reference type `R`, the program is ill-formed.

### 4161(i). Some free functions don't automatically work for program-defined `std::complex<NonFloatingPoint>`

Section: 29.4 [complex.numbers] Status: New Submitter: Jiang An Opened: 2024-09-29 Last modified: 2025-02-07

Priority: 3

View all other issues in [complex.numbers].

View all issues with New status.

Discussion:

`std::complex<NonFloatingPoint>` is possibly a program-defined specialization and thus instantiation of such a specialization can have determined effect. However, it's improbable for implementations to ensure that some free functions, e.g. `abs`, `sin`, `exp`, work for such a specialization.

Moreover, P2819R2 made all `std::complex` specializations tuple-like types, despite that `std::get` overloads need to touch implementation details and thus don't work for program-defined specializations.

It seems better to make only `complex<floating-point>` tuple-like types, and add Constraints to some free functions that can't be guaranteed to work to only accept cases where `T` is a cv-unqualified floating-point type. However, it's unclear to me how to handle cases where the implementation intentionally supports `complex<X>` (where `X` is not a program-defined type) as extension, and whether Mandates should be used.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"Program-defined specializations of `std::complex` (i.e. explicit specializations and program-defined partial specializations) seem unsupportably broken. We should say so explicitly until somebody writes a paper to fix them."

Proposed resolution:

### 4162(i). Worst time complexity of non-parallel versions of `nth_element` is underspecified

Section: 26.8.3 [alg.nth.element] Status: New Submitter: Jiang An Opened: 2024-09-29 Last modified: 2024-10-09

Priority: 3

View all other issues in [alg.nth.element].

View all issues with New status.

Discussion:

Currently, 26.8.3 [alg.nth.element] doesn't specify the worst time complexity for `nth_element` without `ExecutionPolicy` parameter, which seemingly allows a complexity that is `𝒪(N2)` or even worse. Presumably we should make the worst time complexity consistent between parallel and non-parallel versions. For `sort`, LWG 713(i) already strengthened complexity requirements.

[2024-10-09; Reflector poll]

Set priority to 3 after reflector poll.

"This seems to require changes to implementations for them to meet this complexity guarantee."

Proposed resolution:

This wording is relative to N4988.

1. Modify 26.8.3 [alg.nth.element] as indicated:

   >     template<class RandomAccessIterator>
   >       constexpr void nth_element(RandomAccessIterator first, RandomAccessIterator nth,
   >                                  RandomAccessIterator last);
   >     template<class ExecutionPolicy, class RandomAccessIterator>
   >       void nth_element(ExecutionPolicy&& exec,
   >                        RandomAccessIterator first, RandomAccessIterator nth,
   >                        RandomAccessIterator last);
   >     template<class RandomAccessIterator, class Compare>
   >       constexpr void nth_element(RandomAccessIterator first, RandomAccessIterator nth,
   >                                  RandomAccessIterator last, Compare comp);
   >     template<class ExecutionPolicy, class RandomAccessIterator, class Compare>
   >       void nth_element(ExecutionPolicy&& exec,
   >                        RandomAccessIterator first, RandomAccessIterator nth,
   >                        RandomAccessIterator last, Compare comp);
   >     template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
   >              class Proj = identity>
   >       requires sortable<I, Comp, Proj>
   >       constexpr I
   >         ranges::nth_element(I first, I nth, S last, Comp comp = {}, Proj proj = {});

   > > […]

   > > -5- Complexity: For the overloads with no `ExecutionPolicy`, linear on average. For the overloads with an `ExecutionPolicy`, `𝒪(N)` applications of the predicate, and `𝒪(N log N)` swaps, where `N = last - first`. For the overloads with no `ExecutionPolicy`, `𝒪(N)` on average.

### 4165(i). Should swapping a built-in array or `std::array` with itself result in UB?

Section: 18.4.9 [concept.swappable], 22.2.2 [utility.swap], 23.3.3.3 [array.members] Status: New Submitter: Jiang An Opened: 2024-10-13 Last modified: 2025-06-13

Priority: 3

View other active issues in [concept.swappable].

View all other issues in [concept.swappable].

View all issues with New status.

Discussion:

Currently, the `std::swap` overload for built-in arrays, the `swap` member function of `std::array`, the `std::swap` overload for `std::array`, and the expression-equivalent-to operation of `std::ranges::swap` for built-in arrays are specified to be equivalent to performing `std::(ranges::)swap_ranges`. As `swap_ranges` functions require that the swapped ranges shall not overlap (26.7.3 [alg.swap]/2), swapping a built-in array or a nonzero-length `std::array` with itself results in undefined behavior.

It seems that no other swap operation in the standard library has precondition that both arguments don't refer to the same object. Should we make self-swapping of arrays well-defined? If so, perhaps we should change the semantics to plain loops that execute `swap` or `ranges::swap` on elements.

[2025-06-13; Reflector poll]

Set priority to 3 after reflector poll.

Instead of a loop I'd prefer something like:

> Effects: If `addressof(a) == addressof(b)`, no effects. Otherwise, equivalent to `swap_ranges(begin(a), end(a), begin(b))`.

Proposed resolution:

### 4167(i). Use of "smaller" and "larger" in `min`, `max`, and `minmax` is unclear

Section: 26.8.9 [alg.min.max] Status: New Submitter: Casey Carter Opened: 2024-10-20 Last modified: 2025-06-13

Priority: 3

View other active issues in [alg.min.max].

View all other issues in [alg.min.max].

View all issues with New status.

Discussion:

Editorial issue #6747 finds it inconsistent that the wording for the `max`, `min`, and `minmax` algorithms uses "larger" and "smaller" - terms normally applied to physical quantities - to refer to relationships between values which we term "greater" and "lesser" everywhere else in the Working Paper. Using "greater" and "lesser" would make it no less (pun intended) of a problem that we describe the ordering imposed by an arbitrary binary predicate as if it is a less-than ordering.

For example, 26.8.9 [alg.min.max] para 2 says that `std::ranges::min(13, 42, std::greater{})` "Returns: The smaller value. Returns the first argument when the arguments are equivalent." The smaller of 13 and 42 is 13, which is not what this call yields. The reader is supposed to somehow know that "The smaller value" actually means "the value we'd call the lesser if the arguments were numbers and `comp` described a less-then ordering." It would be clearer and more concise to simply say it returns `b` if `invoke(comp, invoke(proj, b), invoke(proj, a))` yields `true` and `a` otherwise.

[2025-06-13; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4993.

1. Modify 26.8.9 [alg.min.max] as indicated:

   >     template<class T>
   >       constexpr const T& min(const T& a, const T& b);
   >     template<class T, class Compare>
   >       constexpr const T& min(const T& a, const T& b, Compare comp);
   >     template<class T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr const T& ranges::min(const T& a, const T& b, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -1- Preconditions: For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -2- Returns: The smaller value. Returns the first argument when the arguments are equivalent. Effects: Equivalent to:

   >     return invoke(comp, invoke(proj, b), invoke(proj, a)) ? b : a;

   -3- Complexity: Exactly one comparison and two applications of the projection, if any.

   -4- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr T min(initializer_list<T> r);
   >     template<class T, class Compare>
   >       constexpr T min(initializer_list<T> r, Compare comp);
   >     template<copyable T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr T ranges::min(initializer_list<T> r, Comp comp = {}, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   >       requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
   >       constexpr range_value_t<R>
   >         ranges::min(R&& r, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -5- Preconditions: `ranges::distance(r) > 0`. For the overloads in namespace `std`, `T` meets the `Cpp17CopyConstructible` requirements (Table [tab:cpp17.copyconstructible]). For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -6- Returns: The smallest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the smallest. A copy of the leftmost element `e` in the input range `r` for which `bool(invoke(comp, invoke(proj, x), invoke(proj, e)))` is `false` for all elements `x` in `r`.

   -7- Complexity: Exactly `ranges::distance(r) - 1` comparisons and twice as many applications of the projection, if any.

   -8- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr const T& max(const T& a, const T& b);
   >     template<class T, class Compare>
   >       constexpr const T& max(const T& a, const T& b, Compare comp);
   >     template<class T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -9- Preconditions: For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -10- Returns: The larger value. Returns the first argument when the arguments are equivalent. Effects: Equivalent to:

   >     return invoke(comp, invoke(proj, a), invoke(proj, b)) ? b : a;

   -11- Complexity: Exactly one comparison and two applications of the projection, if any.

   -12- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr T max(initializer_list<T> r);
   >     template<class T, class Compare>
   >       constexpr T max(initializer_list<T> r, Compare comp);
   >     template<copyable T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr T ranges::max(initializer_list<T> r, Comp comp = {}, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   >       requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
   >       constexpr range_value_t<R>
   >         ranges::max(R&& r, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -13- Preconditions: `ranges::distance(r) > 0`. For the overloads in namespace `std`, `T` meets the `Cpp17CopyConstructible` requirements (Table [tab:cpp17.copyconstructible]). For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -14- Returns: The largest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the largest. Returns a copy of the leftmost element `e` in the input range `r` for which `bool(invoke(comp, invoke(proj, e), invoke(proj, x)))` is `false` for all elements `x` in `r`.

   -15- Complexity: Exactly `ranges::distance(r) - 1` comparisons and twice as many applications of the projection, if any.

   -16- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr pair<const T&, const T&> minmax(const T& a, const T& b);
   >     template<class T, class Compare>
   >       constexpr pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp);
   >     template<class T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr ranges::minmax_result<const T&>
   >         ranges::minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -17- Preconditions: For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -18- Returns: `{b, a}` if `b` is smaller than `a` `bool(invoke(comp, invoke(proj, b), invoke(proj, a)))` is `true` , and `{a, b}` otherwise.

   -19- Complexity: Exactly one comparison and two applications of the projection, if any.

   -20- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

   >     template<class T>
   >       constexpr pair<T, T> minmax(initializer_list<T> t);
   >     template<class T, class Compare>
   >       constexpr pair<T, T> minmax(initializer_list<T> t, Compare comp);
   >     template<copyable T, class Proj = identity,
   >              indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
   >       constexpr ranges::minmax_result<T>
   >         ranges::minmax(initializer_list<T> r, Comp comp = {}, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   >       requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
   >       constexpr ranges::minmax_result<range_value_t<R>>
   >         ranges::minmax(R&& r, Comp comp = {}, Proj proj = {});

   -?- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   -21- Preconditions: `ranges::distance(r) > 0`. For the overloads in namespace `std`, `T` meets the `Cpp17CopyConstructible` requirements (Table [tab:cpp17.copyconstructible]). For the first form, `T` meets the `Cpp17LessThanComparable` requirements (Table [tab:cpp17.lessthancomparable]).

   -22- Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy of the leftmost element with the smallest value in the input range `r` for which `bool(invoke(comp, invoke(proj, e), invoke(proj, x)))` is `false` for all elements `e` in `r`, and `y` is a copy of the rightmost element with the largest value in the input range in `r` for which `bool(invoke(comp, invoke(proj, y), invoke(proj, e)))` is `false` for all elements `e` in `r` .

   -23- Complexity: At most (3/2)`ranges::distance(r)` applications of the corresponding predicatecomparisons and twice as many applications of the projection, if any.

   -24- Remarks: An invocation may explicitly specify an argument for the template parameter `T` of the overloads in namespace `std`.

### 4168(i). `std::start_lifetime_as` inadvertently has undefined behavior due to use of `std::bit_cast`

Section: 20.2.6 [obj.lifetime] Status: New Submitter: Jan Schultke Opened: 2024-10-23 Last modified: 2025-10-22

Priority: 3

View other active issues in [obj.lifetime].

View all other issues in [obj.lifetime].

View all issues with New status.

Discussion:

Consider the motivating example from P2590R2: Explicit lifetime management:

>     struct X { int a, b; };

>     X* make_x() {
>       X* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X));
>       p->a = 1;
>       p->b = 2;
>       return p;
>     }

Assuming that `myMalloc` does not initialize the bytes of storage, this example has undefined behavior because the value of the resulting object of trivially copyable type `X` is determined as if by calling `std::bit_cast<X>(a)` for the implicitly-created object `a` of type `X` (20.2.6 [obj.lifetime] paragraph 3), whose object representation is filled with indeterminate bytes obtained from `myMalloc`. Such a call to `std::bit_cast` has undefined behavior because `std::bit_cast` does not tolerate the creation of an `int` where bits in the value representation are indeterminate (22.11.3 [bit.cast] paragraph 2), and such an `int` is the smallest enclosing object of some of the indeterminate bits.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

"NAD, it says the storage is not accessed, so no UB from reading indeterminate bits."

"The UB comes from `bit_cast`'s Preconditions, not from reading an indeterminate value."

Core should review any change here.

Proposed resolution:

This wording is relative to N4993.

1. Modify 20.2.6 [obj.lifetime] as indicated:

   > [Drafting note: The proposed resolution does not alter the behavior for erroneous bits. Therefore, a call to `std::start_lifetime_as` may have erroneous behavior when used on storage with indeterminate bits, despite not accessing that storage. An alternative resolution would be to produce objects whose value is erroneous.]

   >     template<class T>
   >       T* start_lifetime_as(void* p) noexcept;
   >     template<class T>
   >       const T* start_lifetime_as(const void* p) noexcept;
   >     template<class T>
   >       volatile T* start_lifetime_as(volatile void* p) noexcept;
   >     template<class T>
   >       const volatile T* start_lifetime_as(const volatile void* p) noexcept;

   > > -1- Mandates: […]

   > > -2- Preconditions: […]

   > > -3- Effects: Implicitly creates objects (6.8.2 [intro.object]) within the denoted region consisting of an object `a` of type `T` whose address is `p`, and objects nested within `a`, as follows: The object representation of `a` is the contents of the storage prior to the call to `start_lifetime_as`. The value of each created object `o` of trivially copyable type (6.9.1 [basic.types.general]) `U` is determined in the same manner as for a call to `bit_cast<U>(E)` (22.11.3 [bit.cast]), where `E` is an lvalue of type `U` denoting `o`, except that the storage is not accessed and that for each indeterminate bit `b` in the value representation of the result, the smallest object containing that bit `b` has indeterminate value where the behavior would otherwise be undefined. The value of any other created object is unspecified.

### 4173(i). Better term for "references, pointers and iterators to elements"

Section: 23 [containers] Status: New Submitter: Jonathan Wakely Opened: 2024-11-21 Last modified: 2025-02-07

Priority: 4

View other active issues in [containers].

View all other issues in [containers].

View all issues with New status.

Discussion:

The Containers clause often uses "references, pointers, or iterators" which is verbose, and needs to be said in full whenever talking about iterator invalidation. It would be helpful to have a term of art that refers to all of them, something like "element references" or to avoid any confusion with actual references, "element indicators". Maybe "element handles" but that could be confused with node handles for associative containers, and (based on Wikipedia) has connotations of additional indirection, and something which would not be invalidated when the underlying storage changes.

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll.

"Maybe 'pointer to elements' or a longer phrase that includes the verb 'invalidates', which has special meaning in this section."

"Note that there are cases where we invalidate iterators but not pointers/references."

"Maybe define 'addresses' to mean 'pointers and references' since they're always invalidated at the same time, but iterators are sometimes separate."

"Referential element accessors"

"Define 'pointer-invalidating' (for both pointers and references) and 'iterator-invalidating', and say that the former always implies the latter. Maybe also introduce antonyms 'pointer-preserving' and 'iterator-preserving'."

"Should be defined in terms of affected elements, e.g. 'pointer-invalidating for any erased elements'."

Proposed resolution:

### 4174(i). How does [atomics.order] p3 apply when then modification is an initialization?

Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2024-11-13 Last modified: 2025-02-07

Priority: 3

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with SG1 status.

Discussion:

Consider this example

>     std::atomic<int> v = 0;
>     // thread 1:
>     v.load(std::memory_order::seq_cst);
>     //thread 2:
>     v.store(1,std::memory_order::seq_cst);

If the load operation reads the value `0`, how are load and store operations ordered in the single total order? According to 32.5.4 [atomics.order] p3 (emphasize mine)

> An atomic operation A on some atomic object M is coherence-ordered before another atomic operation B on M if

> 1. […]

> 2. (3.3) — A and B are not the same atomic read-modify-write operation, and there exists an atomic modification X of M such that A reads the value stored by X and X precedes B in the modification order of M, or

According to 32.5.8.2 [atomics.types.operations] p3 (emphasize mine)

> Effects: Initializes the object with the value `desired`. Initialization is not an atomic operation (6.10.2 [intro.multithread]).

So, how does 32.5.4 [atomics.order] p3 apply to this example such that the load operation precedes the store operation in the single total order S?

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll. Send to SG1.

LWG found the issue unclear and felt it was missing context that would help understand it properly.

In cplusplus/CWG/issues/641 the following example was given:

>     std::atomic<bool> a = false;
>     std::atomic<bool> b = false;
>     int v = 0;
>     // thread 1:
>     a.store(true, seq_cst);
>     if(b.load(seq_cst)== false){
>        v = 1;  // #1
>     }
>     //thread 2:
>     b.store(true, seq_cst);
>     if(a.load(seq_cst)== false){
>        v = 2; // #2
>     }

> To prove whether #1 and #2 can have data race, we should prove whether it's possible that `a` and `b` simultaneously read `false`. This proof equals whether there can be a valid single total order in this case. To determine the order of `b.load` and `b.store` when `b.load` reads the initialization value `false`, 32.5.4 [atomics.order] p3.3 should apply here. However, the initialization is not an atomic modification such that `X` cannot be that value.

A possible fix is to amend 32.5.4 [atomics.order]/3.3 to say something like this:

> (3.3) A and B are not the same atomic read-modify-write operation, and either
> 1. (3.3.1) there exists an atomic modification X of M such that A reads the value stored by X and X precedes B in the modification order of M, or
> 2. (3.3.2) A reads the initial value of X and B modifies M, or

Proposed resolution:

### 4176(i). Refer back to container requirements when extending them

Section: 23 [containers] Status: New Submitter: Jonathan Wakely Opened: 2024-11-22 Last modified: 2025-02-07

Priority: 4

View other active issues in [containers].

View all other issues in [containers].

View all issues with New status.

Discussion:

We sometimes give detailed specifications of container members which add additional specification to the common requirements in 23.2.2 [container.requirements.general], for example 23.3.13.5 [vector.modifiers] defines `vector::erase` without actually saying it erases any elements. The actual effects of `erase` are given in 23.2.4 [sequence.reqmts].

Authors of library wording often struggle with this non-local form of specification, where we sometimes do spell out container member functions in full, and sometimes rely on distant wording that applies to all containers. It would be easier if `vector::erase` referred back to 23.2.4 [sequence.reqmts].

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

### 4177(i). §[atomics.order] p8 "circularly depend on their own computation" is unclear for loop

Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2024-11-29 Last modified: 2025-02-07

Priority: 4

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with SG1 status.

Discussion:

32.5.4 [atomics.order] p8 and p9 gave two paradigmatic examples of how "circularly depend on their own computation" means. However, consider this example:

>     std::atomic<int> x = 0, y = 2;

>     // thread 1:
>     if (y.load(relaxed) == 1) { // #1
>       x.store(1, relaxed); // #2
>     }

>     //thread 2:
>     int pre = x.load(relaxed); // #3
>     while (pre != 0) {
>       if (x.compare_exchange_strong(pre, pre + 1, acquire, relaxed)) {  // #4
>         break;
>       }
>     }
>     y.store(1, relaxed); // #5

when both `#1` and `#3` read `1`, is this a kind of OOTA? `#3` depends on `#2`, `#2` depends on `#1`, `#1` depends on `#5`, and the execution of `#5` depends on the exiting of the loop, which in turn initially depends on `pre`.

The loop can never execute, exit after certain iterations, or be a long-time-running without exiting (i.e. `cmpxchg` keeps failing). So, it is unclear whether the execution of `#5` depends on the loop. However, it resembles the `spin-loop` (a failed `cmpxchg` is a pure load with a relaxed load), and the subsequent codes won't execute until the loop exits. So, the scenario of spin-lock seems to agree that the code after a loop depends on the loop(regardless of whether the loop can quickly exit or be a long-time-run loop).

From this perspective, the `while` case is something like the `if`, for `if`, the condition is not `true`, and the code thereof cannot be executed. Similarly, a code after a while cannot be executed if the loop doesn't exit.

Suggested resolution:

Either accurately specify what "circularly depend on their own computation" means, or add a paradigmatic example regarding loop to indicate what it means.

[Additional discussion from submitter:]

I meant, that for a classic spin-lock, the code after the loop won't be executed until the loop exits, and the operation is just a pure load with relaxed memory order during the busy wait. The loop for a spin-lock can have three possible cases:

1. CAS immediately succeeds
2. the loop exits after certain times of iterations
3. the loop cannot be exited(e.g. the lock is not released)

The compiler cannot assume which case the loop for the spin-lock is. The code after the loop won't be reordered(during the busy waiting with a relaxed memory order); otherwise, implementing the spin-lock would be an issue based on the assumption that the codes after the loop wouldn't be reached when the loop was busy waiting.

In this example, the loop has the same possible cases as the loop in the spin-lock. So, the execution of `#5` seems to depend on the loop and the loop depends on the `pre`. why I mention the spin-lock just is to justify that the code after the loop won't be reordered in both compile-time and runtime even though the condition of the loop is a pure load with a relaxed memory order(otherwise, the foundation for which the busy-waiting of a spink-lock is based on won't be true.). So, the loop has a similar effect to the `if` statement on the control flow: the code in the block of the `if` statement won't be executed if the condition is false, as well, the code after the loop won't be executed if the loop hasn't yet exited.

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll. Send to SG1.

"Should the example use `unsigned` so there's no chance of UB due to `int` overflowing?"

"Contrived example showing well-known fact that OOTA prohibition is just hand-waving. N3786 introduced the current wording and made no secret of that. P1217 explored the issue further, as have other SG1 papers. Hard problems. Solutions welcome. Don't think this is the place to focus however."

Proposed resolution:

### 4180(i). Inconsistent constraints on `flat_foo::emplace`

Section: 23.6.12.5 [flat.multiset.modifiers], 23.6.8.7 [flat.map.modifiers] Status: New Submitter: Arthur O'Dwyer Opened: 2024-12-09 Last modified: 2025-02-07

Priority: 3

View all issues with New status.

Discussion:

The usual pattern in 23 [containers] is that `x.emplace(args...)` has a precondition (23.2.4 [sequence.reqmts] p20, 23.2.7.1 [associative.reqmts.general] p48) but no Constraints element. That is, `emplace` is not SFINAE-friendly. And it has only the one overload, so it doesn't need a constraint for purposes of overload resolution.

No Constraints on `emplace`: `deque` (23.3.5.4 [deque.modifiers]), `list` (23.3.11.4 [list.modifiers]), `vector` (23.3.13.5 [vector.modifiers]), `containers` (23.2.4 [sequence.reqmts] p19), `associative containers` (23.2.7.1 [associative.reqmts.general] p47), `unordered containers` (23.2.8.1 [unord.req.general] p78), `priority_queue` (23.6.4.4 [priqueue.members] p5), `optional` (22.5.3.4 [optional.assign] p29).

Constraints on `emplace`: `flat_map` (23.6.8.7 [flat.map.modifiers] p1), `flat_multiset` (23.6.12.5 [flat.multiset.modifiers] p1), `any` (22.7.4.4 [any.modifiers] p1), `expected` (22.8.6.4 [expected.object.assign] p16), `variant` (22.6.3.5 [variant.mod] p1).

I believe a Constraints element was accidentally copy-pasted from the spec of `flat_map::insert(P&&)` — which does need the constraint because it's part of `insert`'s large overload set — to `flat_map::emplace`, and then from there to `flat_multiset::emplace`. The constraint is already (correctly) absent `from flat_set::emplace`.

While we're touching this paragraph, also resolve the vague word "initializes" to "direct-non-list-initializes." Editorially, `pair<…>` is a verbose way to spell the `value_type` of a `flat_map`; we should be consistent and just say `value_type`.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll. See reflector discussion for comments about proposed resolution.

Proposed resolution:

This wording is relative to N4993.

> [Drafting note: 23.6.11.5 [flat.set.modifiers] is already OK as far as this issue is concerned: it has no wording for `emplace`.

> [flat.multimap.modifiers] is already OK ditto: it does not exist. ]

1. Modify 23.6.12.5 [flat.multiset.modifiers] as indicated:

   >     template<class... Args> iterator emplace(Args&&... args);

   > > -1- MandatesConstraints: `is_constructible_v<value_type, Args...>` is `true`.

   > > -2- Effects: First, direct-non-list-initializes an object `t` of type `value_type` with `std::forward<Args>(args)...`, then inserts `t` as if by:

   > > >     auto it = ranges::upper_bound(c, t, compare);
   > > >     c.insert(it, std::move(t));

   > > -3- Returns: An iterator that points to the inserted element.

2. Modify 23.6.8.7 [flat.map.modifiers] as indicated:

   >     template<class... Args> pair<iterator, bool> emplace(Args&&... args);

   > > -1- MandatesConstraints: `is_constructible_v<value_typepair<key_type, mapped_type>, Args...>` is `true`.

   > > -2- Effects: First, direct-non-list-iInitializes an object `t` of type `value_typepair<key_type, mapped_type>` with `std::forward<Args>(args)...`; if the map already contains an element whose key is equivalent to `t.first`, `*this` is unchanged. Otherwise, equivalent to:

   > > >     auto key_it = ranges::upper_bound(c.keys, t.first, compare);
   > > >     auto value_it = c.values.begin() + distance(c.keys.begin(), key_it);
   > > >     c.keys.insert(key_it, std::move(t.first));
   > > >     c.values.insert(value_it, std::move(t.second));

   > > -3- Returns: The `bool` component of the returned pair is `true` if and only if the insertion took place, and the iterator component of the pair points to the element with key equivalent to `t.first`.

### 4181(i). Some ranges have negative `ssize`

Section: 24.7 [iterator.range], 25.3.11 [range.prim.ssize] Status: New Submitter: Casey Carter Opened: 2024-12-13 Last modified: 2025-02-07

Priority: 3

View all other issues in [iterator.range].

View all issues with New status.

Discussion:

There exist range types `R` whose size is representable by neither `ptrdiff_t` nor `make-signed-like-t<ranges::range_size_t<R>>`, specifically `views::iota(size_t(0), size_t(-1))`. It's unfortunate that `std::ssize` and `ranges::ssize` produce a negative size for such ranges even when their difference type is capable of representing their size (see demo). Perhaps the `ssize` overloads should return `static_cast<common_type_t<ptrdiff_t, iter_difference_t<decltype(meow.begin())>>>(meow.size())` instead when the argument `meow` models `range`?

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"The behaviour for Ranges regressed due to LWG 3403(i). It might have been a mistake to broaden the domain of `ranges::ssize` to match that of `ranges::size`. The latter includes some non-Range arguments to ease the transition from `std::size`, but thjere was no existing body of pre-C++20 code calling `std::ssize` that needed a transition path."

Proposed resolution:

### 4183(i). `subrange` should provide `data()`

Section: 25.5.4.1 [range.subrange.general], 25.5.4.1 [range.subrange.general] Status: New Submitter: Hewill Kang Opened: 2024-12-16 Last modified: 2025-02-07

Priority: 4

View other active issues in [range.subrange.general].

View all other issues in [range.subrange.general].

View all issues with New status.

Discussion:

Currently, only four view classes in `<ranges>` explicitly provide `data()` members.

Two of them are `empty_view` and `single_view`, because their `data()` is always valid and can be marked `noexcept`.

The remaining two are `ref_view` and `owning_view` with constrained `data()`, which is redundant since `data()` can always obtained from `view_interface` when the underlying range is contiguous. I suspect this is because `ranges::data` is more efficient.

However, `subrange` does not have a `data()` member, which seems worth considering because this function can always be `noexcept` given that `to_address` is always `noexcept`.

[2025-02-07; Reflector poll]

Set priority to 4 after reflector poll.

Lots or NAD votes. "If we care about `data` being noexcept, we should add conditional noexcept to `view_interface` overloads. Seems like design."

"I don't care about noexcept (impls can strengthen it if it matters), but it's a good idea to avoid the extra iterator copy."

Proposed resolution:

This wording is relative to N5001.

1. Modify 25.5.4.1 [range.subrange.general] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<input_or_output_iterator I, sentinel_for<I> S = I, subrange_kind K =
   >           sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized>
   >         requires (K == subrange_kind::sized || !sized_sentinel_for<S, I>)
   >       class subrange : public view_interface<subrange<I, S, K>> {
   >         […]
   >         constexpr bool empty() const;
   >         constexpr make-unsigned-like-t<iter_difference_t<I>> size() const
   >           requires (K == subrange_kind::sized);
   >         constexpr auto data() const noexcept requires contiguous_iterator<I>;
   >         […]
   >       };
   >       […]
   >     }

2. Modify 25.5.4.3 [range.subrange.access] as indicated:

   >     constexpr make-unsigned-like-t<iter_difference_t<I>> size() const
   >         requires (K == subrange_kind::sized);

   > > -5- Effects:

   > > […]

   >     constexpr auto data() const noexcept requires contiguous_iterator<I>;

   > > -?- Effects: Equivalent to: `return to_address(begin_);`

### 4185(i). Ill-formed, no diagnostic required on runtime behavior

Section: 29.9.7 [linalg.helpers], 18.4.9 [concept.swappable], 24.3.3.2 [iterator.cust.swap] Status: New Submitter: Jiang An Opened: 2024-12-18 Last modified: 2025-02-07

Priority: 3

View all issues with New status.

Discussion:

Several exposition-only function objects in 29.9.7 [linalg.helpers] are specified with strange wording — "If the function selected by overload resolution does not return […], the program is ill-formed, no diagnostic required;". These conditions don't seem to be static properties because what the selected function returns can be completely dependent on the runtime environment and input.

Likewise, 18.4.9 [concept.swappable] and 24.3.3.2 [iterator.cust.swap] also contain such weird usage. Presumably we should say the behavior is undefined in these cases.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4192(i). §[ios.base.cons] `ios_base` members may not have indeterminate values after construction

Section: 31.5.2.8 [ios.base.cons], 31.5.2.4 [ios.base.locales] Status: New Submitter: S. B. Tam Opened: 2025-01-12 Last modified: 2025-02-07

Priority: 3

View all other issues in [ios.base.cons].

View all issues with New status.

Discussion:

31.5.2.8 [ios.base.cons] specifies that

> Each `ios_base` member has an indeterminate value after construction.

However

- If the `ios_base` object has static storage duration, the members would have been zero-initialized.

- If the `ios_base` object has automatic storage duration, the members would have erroneous values.

In either case, the constructor cannot cause the members to have indeterminate values after construction.

A related problem is that 31.5.2.4 [ios.base.locales] requires `ios_base::getloc` to return:

> If no locale has been imbued, a copy of the global C++ locale, `locale()`, in effect at the time of construction.

However, according to 31.5.2.8 [ios.base.cons], the corresponding member is not initialized until `basic_ios::init` is called.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"First part is NAD, can use e.g. placement new to force indeterminate values, and it's not observable by well-defined progams anyway."

Proposed resolution:

This wording is relative to N5001.

1. Modify 31.5.2.4 [ios.base.locales] as indicated:

   >     ios_base();

   > > -1- Effects: Each `ios_base` member has an unspecified (possibly indeterminate or erroneous) value after construction. The object's members shall be initialized by calling `basic_ios::init` before the object's first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.

2. Modify 31.5.2.8 [ios.base.cons] as indicated:

   >     locale getloc() const;

   > > -4- Returns: If no locale has been imbued, a copy of the global C++ locale, `locale()`, in effect at the time of constructionthe last `basic_ios::init` call on the current object. Otherwise, returns the imbued locale, to be used to perform locale-dependent input and output operations.

### 4193(i). §[time.zone.db] the specification uses the undefined term "thread-safe"

Section: 30.11.2 [time.zone.db] Status: New Submitter: S. B. Tam Opened: 2025-01-16 Last modified: 2025-02-07

Priority: 3

View all issues with New status.

Discussion:

N5001 30.11.2.2 [time.zone.db.list] p3:

> Synchronization: This operation is thread-safe with respect to `reload_tzdb()`.

N5001 30.11.2.3 [time.zone.db.access] p2:

> Synchronization: It is safe to call this function from multiple threads at one time.

N5001 30.11.2.4 [time.zone.db.remote] p3:

> Synchronization: This function is thread-safe with respect to `get_tzdb_list().front()` and `get_tzdb_list().erase_after()`.

The standard does not define what "thread-safe" means, which makes the meaning of these sentences unclear. Does it mean that "concurrent calls do not introduce data races", or does it additionally require a single total order on these operations? In either case, it should be specified clearly.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"We're missing a guarantee that `reload_tzdb()` strongly happens before a call to `get_tzdb_list().front()` that retrieves a newly added element. Otherwise accessing any `time_zone` obtained from `get_tzdb_list().front()` could race with the call to `reload_tzdb()` that writes the front of the list."

Proposed resolution:

### 4197(i). Complexity of `std::visit` with immediate functions

Section: 22.6.7 [variant.visit] Status: New Submitter: Jiang An Opened: 2025-01-26 Last modified: 2025-01-30

Priority: 2

View all other issues in [variant.visit].

View all issues with New status.

Discussion:

`std::visit` generally needs to be implemented with "vtables" that contain function pointers. When `std::visit` needs to call an immediate function (e.g. when passing a lambda whose `operator()` is `consteval`), the vtable will contain a pointer to an immediate-escalated function, which forbids the vtable from being a `constexpr` variable.

In order to properly handle immediate functions in `std::visit`, it seems necessary to form the vtable, or do some non-constant-time lookup each time when calling `std::visit`. In other words, 22.6.7 [variant.visit]/8 seems to be unimplementable when an immediate function is involved.

[2025-01-01; P3603R0 addresses this]

[2025-01-30; Reflector poll]

Set priority to 2 after reflector poll.

Proposed resolution:

This wording is relative to N5001.

1. Modify 22.6.7 [variant.visit] as indicated:

   >     template<class Visitor, class... Variants>
   >       constexpr see below visit(Visitor&& vis, Variants&&... vars);
   >     template<class R, class Visitor, class... Variants>
   >       constexpr R visit(Visitor&& vis, Variants&&... vars);

   > > -1- […] Let `n` be `sizeof...(Variants)`. […]

   > > […]

   > > -8- Complexity: If `n > 1` or any of the aforementioned `INVOKE` operations calls an immediate function, the invocation of the callable object has no complexity requirements. OtherwiseFor `n ≤ 1`, the invocation of the callable object is implemented in constant time, i.e., for `n = 1`, it does not depend on the number of alternative types of `V0`. For `n > 1`, the invocation of the callable object has no complexity requirements.

### 4199(i). constraints on user customizations of standard sender algorithms are incorrectly specified

Section: 33.9.1 [exec.snd.general] Status: New Submitter: Eric Niebler Opened: 2025-02-03 Last modified: 2025-02-07

Priority: 1

View all issues with New status.

Discussion:

Imported from cplusplus/sender-receiver #308.

33.9.1 [exec.snd.general]/p1 reads:

> Subclauses 33.9.11 [exec.factories] and 33.9.12 [exec.adapt] define customizable algorithms that return senders. Each algorithm has a default implementation. Let `sndr` be the result of an invocation of such an algorithm or an object equal to the result (18.2 [concepts.equality]), and let `Sndr` be `decltype((sndr))`. Let `rcvr` be a receiver of type `Rcvr` with associated environment `env` of type `Env` such that `sender_to<Sndr, Rcvr>` is `true`. For the default implementation of the algorithm that produced `sndr`, connecting `sndr` to `rcvr` and starting the resulting operation state (33.3 [exec.async.ops]) necessarily results in the potential evaluation (6.3 [basic.def.odr]) of a set of completion operations whose first argument is a subexpression equal to `rcvr`. Let `Sigs` be a pack of completion signatures corresponding to this set of completion operations. Then the type of the expression `get_completion_signatures(sndr, env)` is a specialization of the class template `completion_signatures` ( [exec.util.cmplsig]), the set of whose template arguments is `Sigs`. If a user-provided implementation of the algorithm that produced `sndr` is selected instead of the default, any completion signature that is in the set of types denoted by `completion_signatures_of_t<Sndr, Env>` and that is not part of `Sigs` shall correspond to error or stopped completion operations, unless otherwise specified.

The emphasized sentence is the problem. Since P2300 got lazy customization, the expression `get_completion_signatures(snd, env)` could dispatch to a customization. We should define a low-level exposition-only `get-completion-signatures` function that does the same as `get_completion_signatures` except without the sender transformation. Then we can express `get_completion_signatures` in terms of that.

[2025-02-07; Reflector poll]

Set priority to 1 after reflector poll.

Proposed resolution:

### 4206(i). Alias template `connect_result_t` should be constrained with `sender_to`

Section: 33.4 [execution.syn] Status: New Submitter: Eric Niebler Opened: 2025-02-04 Last modified: 2026-03-25

Priority: 2

View all other issues in [execution.syn].

View all issues with New status.

Discussion:

Imported from cplusplus/sender-receiver #320.

Since turning `execution::connect(sndr, rcvr)`'s constraints to a mandates, the `connect` customization point is now unconstrained.

But there is at least one place in the algorithms (33.9.12.10 [exec.let]) that is using `connect_result_t` in an immediate context of a function template with the expectation that `connect_result_t<Sndr, Rcvr>` will be well-formed only when `Sndr` and `Rcvr` can actually be connected. with the current definition, `connect_result_t<Sndr, Rcvr>` could very well cause a hard error; it will never cuase a substitution failure.

The solution is to constrain the `connect_result_t` alias template. Just as `completion_signatures_of_t<Sndr, Env>` is constrained with `sender_in<Sndr, Env>`, so too should `connect_result_t<Sndr, Rcvr>` be constrained with `sender_to<Sndr, Rcvr>`.

For reference, the `sender_to` concept is defined as follows:

    template<class Sndr, class Rcvr>
        concept sender_to =
          sender_in<Sndr, env_of_t<Rcvr>> &&
          receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&
          requires (Sndr&& sndr, Rcvr&& rcvr) {
            connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr));
          };

[2025-02-07; Reflector poll]

Set priority to 1 after reflector poll.

[Croydon 2026-03-25; Eric withdrew the issue but Lewis described a related problem on the reflector. Needs paper.]

Proposed resolution:

This wording is relative to N5001.

1. Modify 33.4 [execution.syn] as indicated:

   >       template<class Sndr, class Rcvr>
   >         requires sender_to<Sndr, Rcvr>
   >         using connect_result_t =
   >           decltype(connect(declval<Sndr>(), declval<Rcvr>()));

### 4207(i). Point of reference for `source_location` is not specified when used in an default template argument

Section: 17.8.2.2 [support.srcloc.cons] Status: New Submitter: Cassio Neri Opened: 2025-02-07 Last modified: 2025-10-23

Priority: 3

View all other issues in [support.srcloc.cons].

View all issues with New status.

Discussion:

17.8.2.2 [support.srcloc.cons]/2 in N5001 says nothing about using `source_location::current()` in an initializer of a template parameter. The example below suggests that gcc, clang and msvc seem to agree on this case. It would be nice if the Standard legitimized current practice.

    #include <source_location>

    template <int i = std::source_location::current().line()> // line 3
    struct A {
      static constexpr int value = i;
    };

    template <int i = std::source_location::current().line()> // line 8
    constexpr int f() {
      return i;
    }

    static_assert(A<>::value == 3); // passes
    static_assert(f() == 8); // passes

[2025-02-07; Jonathan provides wording]

For a default argument of a function parameter, `current()` is recommended to return the location of the caller that makes use of that default argument. For a default template argument, the location would be determined by the template's point of instantiation (13.8.4.1 [temp.point]) which would not always do what users expect. Using the location of the default template argument in the template declaration seems sensible and predictable, and matches existing practice. Arguably, this doesn't need to be stated because it's just "exactly where the `current()` call appear in the source". The other cases in the Remarks paragraph are situations where the preferred location is different, because we want to know where it's used, not defined.

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

CWG 2631 clarified the rules for immediate invocation for default function arguments and member initializers. We should consider rewriting Remarks to rely on that, and change most of the existing paragraph into normative note. Also use Recommended practice element, as this is normative recommendation.

The cross reference to 11.4 [class.mem] should be changed to 11.4.1 [class.mem.general].

The treatment of default template arguments is the subject of CWG 1635. An alternative design could be that `source_location` inside default template argument refers to point of use. It was noted that this does not reflect current behavior of compilers.

Proposed resolution:

This wording is relative to N5001.

1. Modify 17.8.2.2 [support.srcloc.cons] as indicated:

   > -2- Remarks: Any call to `current` that appears as a default member initializer (11.4 [class.mem]), or as a subexpression thereof, should correspond to the location of the constructor definition or aggregate initialization that uses the default member initializer. Any call to `current` that appears as a default argument (9.3.4.7 [dcl.fct.default]), or as a subexpression thereof, should correspond to the location of the invocation of the function that uses the default argument (7.6.1.3 [expr.call]). Any call to `current` that appears as a default template argument (13.2 [temp.param]), or as a subexpression thereof, should correspond to the location where the default template argument is specified.

### 4210(i). Issue with `cache_latest_view::iterator`'s reference type

Section: 25.7.34.3 [range.cache.latest.iterator] Status: New Submitter: Hewill Kang Opened: 2025-02-09 Last modified: 2025-06-13

Priority: 3

View all issues with New status.

Discussion:

`cache_latest_view::iterator` can be roughly regarded as an iterator that transforms the original iterator's reference, because its reference is always an lvalue which is `range_reference_t<V>&`, even if the original iterator returns an rvalue.

In this case, it seems a bit odd to still specialize `iter_move` and return the rvalue reference type of the original iterator, because the original reference has changed, so the former may not form a valid common reference with the original rvalue reference.

That is, in some rare cases even if the `cache_latest_view` can be legally constructed, it may not be an `input_range` as `indirectly_readable` is not satisfied.

A contrived example could be:

>     struct I {
>       using value_type = int;
>       using difference_type = int;

>       struct move_only_ref {
>         move_only_ref(const int&);
>         move_only_ref(move_only_ref&&) = default;
>       };

>       move_only_ref operator*() const;
>       I& operator++();
>       void operator++(int);
>     };

>     using R = ranges::cache_latest_view<ranges::subrange<I, unreachable_sentinel_t>>; // ok

>     static_assert(input_iterator<I>);
>     static_assert(ranges::input_range<R>); // failed

It's unclear whether we should change the reference type to `range_reference_t<V>&&` to preserve the original ref-qualifiers as much as possible (I don't see any discussion of this option in the original paper), or not provide a specialized `iter_move`, since it's intuitive to have the default `iter_move` return an rvalue when it always returns an lvalue.

[2025-06-13; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4211(i). IANA time zone database allows links to refer to links

Section: 30.11.2.1 [time.zone.db.tzdb] Status: New Submitter: Jonathan Wakely Opened: 2025-02-22 Last modified: 2025-10-21

Priority: 2

View all issues with New status.

Discussion:

Since October 2022 the IANA Time Zone Database's `tzfile` data format allows a Link to refer to another Link, instead of referring directly to a Zone. The zic(8) man page says:

> The TARGET field should appear as the NAME field in some zone line or as the LINK-NAME field in some link line. The LINK-NAME field is used as an alternative name for that zone; it has the same syntax as a zone line's NAME field. Links can chain together, although the behavior is unspecified if a chain of one or more links does not terminate in a Zone name.

Because chains of links were introduced to `tzfile` after `chrono::tzdb` was standardized, C++ does not properly support chains of links.

The `time_zone_link::target()` member function is required to return "The name of the `time_zone` for which this `time_zone_link` provides an alternative name." This doesn't allow returning the name of another `time_zone_link`, which implies that the implementation should flatten a series of links so that the target is a `time_zone`. However, this discards information which is present in the `tzdb`, so that the information exposed to a C++ program does not preserve the original structure of a chain of links.

The standard could be adjusted to support chains of links by allowing `time_zone_link::target()` to refer to another link, and then altering the algorithm used by `tzdb::locate_zone(string_view)` to find a `time_zone` from a name that might be a zone or a link.

[2025-10-21; Reflector poll.]

Set priority to 2 after reflector poll. Add Jonathan's comments on the resolution:

The goal of the resolution is to permit `time_zone_link::target` to name another link, and not overspecify `tzdb::locate_zone` to require that `tz_l.target()` names a valid zone `tz`. Considered normatively saying what happens if a link chain doesn't terminate in a zone (like the zic(8) man page does), but that seemed like overspecification. We don't currently say what happens if a non-chained link has an invalid target, it's just covered by `locate_zone` failing to find anything. So just added a note saying bad chains are bad.

Not urgent, as Libstdc++ and libc++ already handle this in some form. Links that use the same name as zones are unlikely to be a problem with the official IANA database, probably only relevant for manually edited data. Likewise for cycles in link chains. Maybe just leave to QoI.

Proposed resolution:

This wording is relative to N5001.

1. Modify 30.11.2.1 [time.zone.db.tzdb] as indicated:

   >     const time_zone* locate_zone(string_view tz_name) const;

   > -2- Returns:

   > 1. (2.1) — If `zones` contains an element `tz` for which `tz.name() == tz_name`, a pointer to `tz`;
   > 2. (2.2) — otherwise, if `links` contains an element `tz_l` for which `tz_l.name() == tz_name`, then a pointer to the element `tz` of zones for which `tz.name() == tz_l.target()` the result of `locate_zone(tz_l.target())`. .

   > [Note 1: A `time_zone_link` specifies an alternative name for a `time_zone`. — end note]

   > -3- Throws: If a `const time_zone*` cannot be found as described in the Returns: element, throws a `runtime_error`.

   > [Note 2: On non-exceptional return, the return value is always a pointer to a valid `time_zone`. — end note]

   > -?- Remarks: If both `zones` and `links` contain elements that match `tz_name` then it is unspecified whether `&tz` or `locate_zone(tz_l.target())` is returned.

   > > [Drafting note: This gives flexibility how to implement the lookup in `locate_zone`.]

2. Modify 30.11.9.1 [time.zone.link.overview] as indicated:

   > -1- A `time_zone_link` specifies an alternative name for a `time_zone`. `time_zone_links` are constructed when the time zone database is initialized. The name of a `time_zone_link` can be used as an argument to `tzdb::locate_zone` (30.11.2.1 [time.zone.db.tzdb]). A `time_zone_link` can refer directly to a `time_zone`, or to another `time_zone_link`, forming a chain of links.

3. Modify 30.11.9.2 [time.zone.link.members] as indicated:

   >     string_view name() const noexcept;

   > -1- Returns: The alternative name for the time zone.

   >     string_view target() const noexcept;

   > -2- Returns: The name of the `time_zone` for which this `time_zone_link` provides an alternative name or the name of another `time_zone_link`.

   > [Note 1: `tzdb::locate_zone` follows a chain of links formed when a link's target is the name of a `time_zone_link`, throwing an exception if the chain of links does not terminate in a `time_zone`. — end note]

### 4212(i). Make the round states in [rand.eng.philox] explicit

Section: 29.5.4.5 [rand.eng.philox] Status: New Submitter: Thomas Köppe Opened: 2025-02-12 Last modified: 2025-08-19

Priority: 3

View all other issues in [rand.eng.philox].

View all issues with New status.

Discussion:

The current wording that specifies the operation of the Philox random bit generator seems needlessly vague. We can add precision by defining a few more terms, instead of requiring the reader to fill in the blanks.

Concretely, the variable X' is only vaguely defined at the moment, and the definition of the "r-round network", "rounds", and how they fit together, is somewhat informal and imprecise. The statement that `Philox` "returns the sequence Y = X'" is needlessly ambiguous (what is X' here?).

I propose the change that I drafted at draft/pull/7152: Namely, spell out the meaning of the "rounds" and create a distinct name for every value in every round. This allows us to state the result precisely, and makes it clear how each round computes a new value from the values of the previous rounds.

It seems convenient to change the round counter q to be 1-based (and X(0) is an alias for the initial value, X), so that the final result is X(r).

[2025-06-13; Reflector poll]

Set priority to 3 after reflector poll.

Previous resolution [SUPERSEDED]

This wording is relative to N5001.

1. Modify 29.5.4.5 [rand.eng.philox] as indicated:

   > -2- The generation algorithm returns Y i, the value stored in the i th element of Y after applying the transition algorithm.

   > -3- The state transition is performed as if by the following algorithm:

   > >     i=i+1
   > >     if (i == n) {
   > >       Y=Philox(K, X) // see below
   > >       Z=Z+1         // this updates X
   > >       i=0
   > >     }

   > -4- The `Philox` function maps the length-n/2 sequence K and the length-n sequence X into a length-n output sequence Y. Philox applies an r-round substitution-permutation network to the values in X. A single round of the generation algorithm performs the following steps: That is, there are intermediate values X(0), X(1), …, X(r), where X(0):=X, and for each round q (with q=1,…,r), X(q) is computed from X(q-1) as follows. The output sequence is X(r).

   > 1. (4.1) — The output sequence X' of the previous round (X in case of the first round) is permuted to obtain the intermediate state V:

   >    >     Vj=X'fn(j)

   >    An intermediate state V(q) is obtained by permuting the previous output, V j(q):=X f n(j)(q-1), where j=0,…,n−1, and f n(j) is defined in Table 124.

   > 2. (4.2) — The following computations are applied to the elements of the V sequence: The next output X(q) is computed from the elements of the V(q) as follows. For k=0,…,n/2-1,

   >    1. (4.2.?) — X 2 k+0(q) = mulhi(V 2 k(q),M k,w) xor K k(q) xor V 2 k+1(q), and X 2 k+0 = mulhi(V 2 k,M k,w) xor key k q xor V 2 k+1

   >    2. (4.2.?) — X 2 k+1(q) = mullo(V 2 k(q),M k,w),X 2 k+1 = mullo(V 2 k,M k,w)

   >    where:

   >    1. (4.2.1) — mullo(a, b, w) is the low half of the modular multiplication of a and b: (a⋅b)mod 2 w,

   >    2. (4.2.2) — mulhi(a, b, w) is the high half of the modular multiplication of a and b: (⌊(a⋅b)/2 w⌋),

   >    3. (4.2.3) — k=0,…,n/2−1 is the index in the sequences, K k(q) is the k th round key for round q, K k(q):=(K k+(q-1)⋅C k)mod 2 w,

   >    4. (4.2.4) — q=0,…,r−1 is the index of the round, K k is the k th element of the key sequence K,

   >    5. (4.2.5) — key k q is the k th round key for round q, key k q:=(K k+q⋅C k)mod 2 w,

   >    6. (4.2.6) — K k are the elements of the key sequence K,

   >    7. (4.2.7) — M k is `multipliers[k]`, and

   >    8. (4.2.8) — C k is `round_consts[k]`.

[2025-08-08; Matt Stephanson comments and makes wording improvements with Thomas Köppe]

1. For what it's worth, I believe the new wording correctly describes the algorithm and does not make any substantive changes.

2. As for the wording itself:

   1. Paragraphs 1, 2, and 3 still refer to the output sequence as `Y`, so I don't think it should be removed from the end of the first sentence in p4. On the contrary, to maintain the connection and parallel the "$X^{(0)} := X$" wording, I think the final sentence should also be "The output sequence is $X^{(r)} := Y$".

   2. I can't explain why, but my intuition is that, in bullet (4.2), "the elements of $V^{(q)}$" sounds better than "the elements of the $V^{(q)}$". The second "the" worked with the original wording "the `V` sequence", but "sequence" is omitted in the proposed resolution.

   3. The use of `K` for both the fixed key sequence and the round keys seems potentially confusing. Maybe `R` for "round key" is better?

After discussion with Thomas Köppe there was agreement that (a) should be applied with the modification that we should write it as definition of `Y` and not the other way around, that (b) should be applied as suggested, and that there was no real consensus for proposal (c).

Proposed resolution:

This wording is relative to N5014.

1. Modify 29.5.4.5 [rand.eng.philox] as indicated:

   > -2- The generation algorithm returns Y i, the value stored in the i th element of Y after applying the transition algorithm.

   > -3- The state transition is performed as if by the following algorithm:

   > >     i=i+1
   > >     if (i == n) {
   > >       Y=Philox(K, X) // see below
   > >       Z=Z+1         // this updates X
   > >       i=0
   > >     }

   > -4- The `Philox` function maps the length-n/2 sequence K and the length-n sequence X into a length-n output sequence Y. Philox applies an r-round substitution-permutation network to the values in X. A single round of the generation algorithm performs the following steps: That is, there are intermediate values X(0), X(1), …, X(r), where X(0):=X, and for each round q (with q=1,…,r), X(q) is computed from X(q-1) as follows. The output sequence is Y:=X(r).

   > 1. (4.1) — The output sequence X' of the previous round (X in case of the first round) is permuted to obtain the intermediate state V:

   >    >     Vj=X'fn(j)

   >    An intermediate state V(q) is obtained by permuting the previous output, V j(q):=X f n(j)(q-1), where j=0,…,n−1, and f n(j) is defined in Table 129.

   > 2. (4.2) — The following computations are applied to the elements of the V sequence: The next output X(q) is computed from the elements of V(q) as follows. For k=0,…,n/2-1,

   >    1. (4.2.?) — X 2 k+0(q) = mulhi(V 2 k(q),M k,w) xor K k(q) xor V 2 k+1(q), and X 2 k+0 = mulhi(V 2 k,M k,w) xor key k q xor V 2 k+1

   >    2. (4.2.?) — X 2 k+1(q) = mullo(V 2 k(q),M k,w),X 2 k+1 = mullo(V 2 k,M k,w)

   >    where:

   >    1. (4.2.1) — mullo(a, b, w) is the low half of the modular multiplication of a and b: (a⋅b)mod 2 w,

   >    2. (4.2.2) — mulhi(a, b, w) is the high half of the modular multiplication of a and b: (⌊(a⋅b)/2 w⌋),

   >    3. (4.2.3) — k=0,…,n/2−1 is the index in the sequences, K k(q) is the k th round key for round q, K k(q):=(K k+(q-1)⋅C k)mod 2 w,

   >    4. (4.2.4) — q=0,…,r−1 is the index of the round, K k is the k th element of the key sequence K,

   >    5. (4.2.5) — key k q is the k th round key for round q, key k q:=(K k+q⋅C k)mod 2 w,

   >    6. (4.2.6) — K k are the elements of the key sequence K,

   >    7. (4.2.7) — M k is `multipliers[k]`, and

   >    8. (4.2.8) — C k is `round_consts[k]`.

### 4214(i). Missing Preconditions for `take`/`drop` adaptor

Section: 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview] Status: New Submitter: Hewill Kang Opened: 2025-02-15 Last modified: 2025-10-21

Priority: 3

View all other issues in [range.take.overview].

View all issues with New status.

Discussion:

The `take`/`drop` adaptor does not explicitly require N to be non-negative (although the view class does), which makes it possible for some specialized cases to be well-defined when N is negative, since no Preconditions are violated:

>     auto e = std::views::empty<int>
>            | std::views::take(-1);   // []
>     auto i = std::views::iota(1, 5)
>            | std::views::drop(-1);   // [0, 1, 2, 3, 4]
>     auto r = std::views::repeat('a', 2)
>            | std::views::drop(-1);   // ['a', 'a', 'a']

This is not the intention, we should ban these cases.

Previous resolution [SUPERSEDED]

This wording is relative to N5001.

1. Modify 25.7.10.1 [range.take.overview] as indicated:

   > -2- The name `views::take` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::take(E, F)` is ill-formed. Otherwise, the expression `views::take(E, F)` is expression-equivalent to:

   > 1. (2.?) — Preconditions: `static_cast<D>(F) >= 0` is `true`.

   > 2. (2.1) — if `T` is a specialization of `empty_view` (25.6.2.2 [range.empty.view]), then `((void)F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced.

2. Modify 25.7.12.1 [range.drop.overview] as indicated:

   > -2- The name `views::drop` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::drop(E, F)` is ill-formed. Otherwise, the expression `views::drop(E, F)` is expression-equivalent to:

   > 1. (2.?) — Preconditions: `static_cast<D>(F) >= 0` is `true`.

   > 2. (2.1) — if `T` is a specialization of `empty_view` (25.6.2.2 [range.empty.view]), then `((void)F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced.

[2025-02-24]

Upon reflector discussion many preferred to use instead the new Hardened preconditions: element which have been introduced by the recently voted in P3471R4.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

"Disagreement on whether to introduce Hardened preconditions in this Clause. Some discomfort with adding the element as a bullet point inside a list, no better suggestions though.

Proposed resolution:

This wording is relative to N5001 plus additions from P3471R4.

1. Modify 25.7.10.1 [range.take.overview] as indicated:

   > -2- The name `views::take` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::take(E, F)` is ill-formed. Otherwise, the expression `views::take(E, F)` is expression-equivalent to:

   > 1. (2.?) — Hardened preconditions: `static_cast<D>(F) >= 0` is `true`.

   > 2. (2.1) — if `T` is a specialization of `empty_view` (25.6.2.2 [range.empty.view]), then `((void)F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced.

2. Modify 25.7.12.1 [range.drop.overview] as indicated:

   > -2- The name `views::drop` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::drop(E, F)` is ill-formed. Otherwise, the expression `views::drop(E, F)` is expression-equivalent to:

   > 1. (2.?) — Hardened preconditions: `static_cast<D>(F) >= 0` is `true`.

   > 2. (2.1) — if `T` is a specialization of `empty_view` (25.6.2.2 [range.empty.view]), then `((void)F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced.

### 4216(i). `num_put::do_put` and `void` pointers

Section: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Nikolas Klauser Opened: 2025-02-26 Last modified: 2025-06-13

Priority: 3

View other active issues in [facet.num.put.virtuals].

View all other issues in [facet.num.put.virtuals].

View all issues with New status.

Discussion:

The `num_put::do_put` overloads are defined in terms of `printf`. However, it is not clear what the intended behaviour of pointers is.

While the `num_put` wording makes a quite clear statement that it should be whatever `printf("%p", ptr)` would be, `num_get` is entirely silent on which function should be used. This makes it entirely unclear whether round-tripping is supposed to work. It's also not clear whether `num_put` was just simple to specify via `printf` or whether the intent was that the output matches in all cases. Round-tripping between `num_put` and `num_get` was broken in libc++ until recently. However, to fix that, the output of `num_put::do_put` no longer matches the libc's `printf` in all cases. libstdc++ had this behaviour since at least two decades, indicating that nobody seems to have a problem with `num_put::do_put` and `printf` having different results in some rare cases.

[2025-06-13; Reflector poll]

Set priority to 3 after reflector poll.

We could emphasize that calling `printf` isn't actually intended:

> The representations at the end of stage 1 consists of the `char`'s that would be printed by a call of `printf(s, val)` in a hypothetical implementation, where `s` is the conversion specifier determined above.

Why is there an apostrophe in `char`'s?

Proposed resolution:

This wording is relative to N5001.

1. Modify 28.3.4.3.3.3 [facet.num.put.virtuals] as indicated:

   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, long val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, long long val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, unsigned long val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, unsigned long long val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, double val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, long double val) const;
   >     iter_type do_put(iter_type out, ios_base& str, char_type fill, const void* val) const;

   > > -1- Effects: […]

   > > -2- The details of this operation occur in several stages: […]

   > > -3- Detailed descriptions of each stage follow.

   > > -4- Returns: `out`.

   > > Stage 1: The first action of stage 1 is to determine a conversion specifier. […]

   > > For conversion from `void*` the specifier is `%p`.

   > > The representations at the end of stage 1 consists of the `char`'s that would be printed by a call of `printf(s, val)` where `s` is the conversion specifier determined above, except that any implementation-defined behavior of `printf` may be different from a call to `printf`.

### 4225(i). What should happen when an exception is thrown on resizing `std::deque`, `std::forward_list`, or `std::list`?

Section: 23.3.5.3 [deque.capacity], 23.3.7.5 [forward.list.modifiers], 23.3.11.3 [list.capacity] Status: New Submitter: Jiang An Opened: 2025-03-15 Last modified: 2025-10-17

Priority: 3

View all other issues in [deque.capacity].

View all issues with New status.

Discussion:

Currently, `std::vector` and `std::inplace_vector`'s `resize` functions are specified to have no effects on the container when an exception is throwing on appending. However, such specification seem to be missing for `std::deque`, `std::forward_list`, and `std::list`.

Is such difference intented? Do we want to roll back the status of container when the appending is partially done?

Daniel:

The specific `resize` exception guarantee for `std::vector` came from resolving LWG 2033(i) and were later effectively copied to `std::inplace_vector` because that container's specification should resemble as much as possible that of `std::vector`.

[2025-10-16; Reflector poll]

Set priority to 3 after reflector poll.

[2025-10-16; Jonathan provides wording]

LWG 4106(i) already fixed this for `std::forward_list`. For `std::list` the "If an exception is thrown, there are no effects" wording in 23.3.11.4 [list.modifiers] p2 doesn't apply to `std::list::resize` because it's in a different subclause (23.3.11.3 [list.capacity]). We can fix that though.

[2025-10-17; Comment from Nevin.]

`deque::resize` invalidates all iterators if it causes a map reallocation.

Proposed resolution:

This wording is relative to N5014.

1. Modify 23.3.5.3 [deque.capacity] as indicated:

   >     constexpr void resize(size_type sz);

   > > -1- Preconditions: `T` is Cpp17MoveInsertable and Cpp17DefaultInsertable into `deque`.

   > > -2- Effects: If `sz < size()` is `true`, erases the last `size() - sz` elements from the sequence. Otherwise, appends `sz - size()` default-inserted elements to the sequence. If an exception is thrown, there is no effect on the container.

   >     constexpr void resize(size_type sz, const T& c);

   > > -3- Preconditions: `T` is Cpp17CopyInsertable into `deque`.

   > > -4- Effects: If `sz < size()` is `true`, erases the last `size() - sz` elements from the sequence. Otherwise, appends `sz - size()` copies of `c` to the sequence. If an exception is thrown, there is no effect on the container.

2. Modify 23.3.11.3 [list.capacity] as indicated:

   >     constexpr void resize(size_type sz);

   > > -1- Preconditions: `T` is Cpp17DefaultInsertable into `list`.

   > > -2- Effects: If `size() < sz` is `true`, appends `sz - size()` default-inserted elements to the sequence. If `sz <= size()` is `true`, equivalent to:

   > > >     list<T>::iterator it = begin();
   > > >     advance(it, sz);
   > > >     erase(it, end());

   > > If an exception is thrown, there is no effect on the container.

   >     constexpr void resize(size_type sz, const T& c);

   > > -3- Preconditions: `T` is Cpp17CopyInsertable into `list`.

   > > -4- Effects: As if by Equivalent to:

   > > >     if (sz < size()
   > > >       insert(end(), sz-size(), c);
   > > >     else if (sz < size()) {
   > > >       iterator i = begin();
   > > >       advance(it, sz);
   > > >       erase(it, end());
   > > >     }
   > > >     else
   > > >       ;           // do nothing

### 4226(i). `to_input_view::iterator` cannot be compared to its `const` sentinel

Section: 99 [range.to.input] Status: New Submitter: Hewill Kang Opened: 2025-03-15 Last modified: 2025-06-13

Priority: 2

View all issues with New status.

Discussion:

`to_input_view` was recently added to the working draft by P3137R3.

Consider: (demo):

>     #include <ranges>

>     int main() {
>       auto r = std::views::single(0)
>              | std::views::chunk(1)
>              | std::views::to_input;

>       r.begin() == std::as_const(r).end(); // #1, error
>       r.begin() ==               r.cend(); // #2, error
>     }

In `#1`, `r.begin()` returns `to_input_view<chunk_view>::iterator<false>`, while the latter returns `chunk_view::iterator<true>`. Since the former can only be compared with `chunk_view::iterator<false>` that cannot be converted from `chunk_view::iterator<true>`, the two are incomparable.

This can be fixed by adding the following overload to `to_input_view::iterator`:

>     template<bool OtherConst = !Const>
>       requires sentinel_for<sentinel_t<maybe-const<OtherConst, V>>, iterator_t<Base>>
>     friend constexpr bool operator==(const iterator& x, const sentinel_t<maybe-const<OtherConst, V>>& y)
>     { return x.current_ == y; }

Unfortunately, it still doesn't resolve `#2`, because `r.cend()` returns `basic_const_iterator<chunk_view::iterator<true>>`, which cannot be compared to any non-copyable iterators as its `operator==(const S& s)` requires `S` to be a sentinel type, which rules out `to_input_view::iterator`, so the constraint is not satisfied.

I believe we may need to introduce a custom sentinel class for `to_input_view`.

[2025-06-13; Reflector poll]

Set priority to 2 after reflector poll.

Proposed resolution:

This wording is relative to N5008.

1. Modify 99 [range.to.input.view] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range V>
   > >         requires view<V>
   > >       class to_input_view : public view_interface<to_input_view<V>> {
   > >         V base_ = V();                          // exposition only

   > >         template<bool Const>
   > >         class iterator;                         // exposition only
   > >         template<bool Const>
   > >         class sentinel;                         // exposition only
   > >         […]
   > >       };
   > >       […]
   > >     }

   > […]

   >     constexpr auto end() requires (!simple-view<V>);

   > > -?- Effects: Equivalent to:

   > >     return sentinel<false>(ranges::end(base_));

   >     constexpr auto end() const requires range<const V>;

   > > -4- Effects: Equivalent to:

   > >     return sentinel<true>(ranges::end(base_));

2. Modify [range.to.input.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range V>
   > >         requires view<V>
   > >       template<bool Const>
   > >       class to_input_view<V>::iterator {
   > >         […]
   > >         friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);

   > >         friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x)
   > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
   > >         friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y)
   > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
   > >         […]
   > >       };
   > >     }

   > […]

   >     friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);

   > > -7- Returns: `x.current_ == y`.

   >     friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x)
   >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

   > > -8- Returns: `y - x.current_`.

   >     friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y)
   >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

   > > -9- Returns: `x.current_ - y`.

3. Add [range.to.input.sentinel] after [range.to.input.iterator] as indicated:

   > >     namespace std::ranges {
   > >       template<input_range V>
   > >         requires view<V>
   > >       template<bool Const>
   > >       class to_input_view<V>::sentinel {
   > >         using Base = maybe-const<Const, V>;                         // exposition only
   > >         sentinel_t<Base> end_ = sentinel_t<Base>();                 // exposition only
   > >         constexpr explicit sentinel(sentinel_t<Base> end);          // exposition only

   > >       public:
   > >         sentinel() = default;
   > >         constexpr sentinel(sentinel<!Const> other)
   > >           requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

   > >         constexpr sentinel_t<Base> base() const;

   > >         template<bool OtherConst>
   > >           requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   > >         friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);

   > >         template<bool OtherConst>
   > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   > >         friend constexpr range_difference_t<maybe-const<OtherConst, V>>
   > >           operator-(const iterator<OtherConst>& x, const sentinel& y);

   > >         template<bool OtherConst>
   > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   > >         friend constexpr range_difference_t<maybe-const<OtherConst, V>>
   > >           operator-(const sentinel& x, const iterator<OtherConst>& y);
   > >       };
   > >     }

   >     constexpr explicit sentinel(sentinel_t<Base> end);

   > > -?- Effects: Initializes `end_` with `std::move(end)`.

   >     constexpr sentinel(sentinel<!Const> other)
   >       requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

   > > -?- Effects: Initializes `end_` with `std::move(other.end_)`.

   >     constexpr sentinel_t<Base> base() const;

   > > -?- Effects: Equivalent to: `return end_;`

   >     template<bool OtherConst>
   >       requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   >     friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);

   > > -?- Effects: Equivalent to: `return x.current_ == y.end_;`

   >     template<bool OtherConst>
   >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   >     friend constexpr range_difference_t<maybe-const<OtherConst, V>>
   >       operator-(const iterator<OtherConst>& x, const sentinel& y);

   > > -?- Effects: Equivalent to: `return x.current_ - y.end_;`

   >     template<bool OtherConst>
   >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
   >     friend constexpr range_difference_t<maybe-const<OtherConst, V>>
   >       operator-(const sentinel& x, const iterator<OtherConst>& y);

   > > -?- Effects: Equivalent to: `return x.end_ - y.current_;`

### 4237(i). The standard library iterator adaptor does not handle `iterator_category` correctly

Section: 24.5.3.3 [const.iterators.iterator], 25.7 [range.adaptors] Status: New Submitter: Hewill Kang Opened: 2025-03-27 Last modified: 2025-06-12

Priority: 3

View all other issues in [const.iterators.iterator].

View all issues with New status.

Discussion:

Currently, `basic_const_iterator`, and several range adaptors such as `filter_view`'s iterators provide `iterator_category` only when the underlying iterator models `forward_iterator`, implying that they expect those iterators should have a valid `iterator_category`.

However, this is incorrect because being a `forward_iterator` does not necessarily mean it is a Cpp17InputIterator, it just means that it probably meets the syntactic requirements of Cpp17InputIterator.

Any iterator that specializes `iterator_traits` and provides only `iterator_concept` without `iterator_category` is not a Cpp17InputIterator, for example, `common_iterator` with a `difference_type` of integer-class type.

In this case, instantiating these iterator adaptors will result in a hard error because the `iterator_category` they expect does not exist. The following illustrates the problem (demo):

>     #include <iterator>
>     #include <ranges>

>     int main() {
>       auto r = std::views::iota(0ULL)
>              | std::views::take(5)
>              | std::views::common;

>       static_assert(std::ranges::forward_range<decltype(r)>);

>       std::basic_const_iterator ci(r.begin()); // 'iterator_category': is not a member of 'std::iterator_traits'

>       auto f = r | std::views::filter([](auto) { return true; });
>       auto b = f.begin();                      // 'iterator_category': is not a member of 'std::iterator_traits'
>     }

I believe that checking if the underlying iterator is a `forward_iterator` is not an appropriate mechanism to provide `iterator_category`, but rather checking if its `iterator_traits` specialization provides `iterator_category`.

This issue is somewhat related to LWG 3763(i), which is a further consideration after LWG 3749(i) has been resolved.

[2025-06-12; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4240(i). The formattable type is not a `formattable` type

Section: 28.5.6.3 [format.formattable] Status: New Submitter: Hewill Kang Opened: 2025-04-06 Last modified: 2025-06-12

Priority: 2

View other active issues in [format.formattable].

View all other issues in [format.formattable].

View all issues with New status.

Discussion:

User-specific `formatter`s usually have the following form:

>     template <> struct std::formatter<T> {
>       constexpr auto parse(format_parse_context& ctx)
>         -> format_parse_context::iterator;

>       auto format(const T& value, format_context& ctx) const
>         -> format_context::iterator;
>     };

This is reflected in wording examples such as 28.5.6.4 [format.formatter.spec] bullet 8 or 28.5.6.7 [format.context] bullet 9:

>     #include <format>
>     #include <string>

>     enum color { red, green, blue };
>     const char* color_names[] = { "red", "green", "blue" };

>     template<> struct std::formatter<color> : std::formatter<const char*> {
>       auto format(color c, format_context& ctx) const {
>         return formatter<const char*>::format(color_names[c], ctx);
>       }
>     };

which allows us to format `color` with `std::format("{}", red)`. Unfortunately, even so, the `color` still does not satisfy `std::formattable`.

This is because the concept `formattable` is currently defined as follows:

>     template<class T, class Context,
>              class Formatter = typename Context::template formatter_type<remove_const_t<T>>>
>       concept formattable-with =                // exposition only
>         semiregular<Formatter> &&
>         requires(Formatter& f, const Formatter& cf, T&& t, Context fc,
>                  basic_format_parse_context<typename Context::char_type> pc)
>         {
>           { f.parse(pc) } -> same_as<typename decltype(pc)::iterator>;
>           { cf.format(t, fc) } -> same_as<typename Context::iterator>;
>         };

>     template<class T, class charT>
>       concept formattable =
>         formattable-with<remove_reference_t<T>, basic_format_context<fmt-iter-for<charT>, charT>>;

where `fmt-iter-for<charT>` is an unspecified type that can write `charT`, which for `char` is `back_insert_iterator<string>` and `char*` in libstdc++ and libc++, respectively.

That is, for `color` to satisfy `formattable`, it is necessary to ensure that `cf.format(t, fc)` is well-formed.

However, the `format()` function in the above example takes a `format_context` whose `Out` parameter is internal iterator type, namely `__format::_Sink_iter<char>` and `back_insert_iterator<__format::__output_buffer<char>>` in libstdc++ and libc++, respectively. Since `basic_format_context` with different `Out` parameters cannot be converted to each other, the constraint is not satisfied.

The reason `color` can still be formatted is that `basic_format_arg` checks for `formattable-with<Context>` where `Context` has been correctly specified as `format_context`.

And since `color` is formattable but not `formattable`, this further prevents formatting a range with elements of `color`, because the `formatter` specialization for ranges requires that the element type must be `formattable`. This leads to some inconsistencies (demo):

>     std::println("{}", red); // ok
>     static_assert(std::formattable<color, char>); // fires

>     std::vector<color> v;
>     std::println("{}", v); // not ok

The workaround is to turn the custom `format()` into a template function such as `format(color c, auto& ctx)` or `format(color c, basic_format_context<Out, charT>& ctx)`, However, this seems mandate users to always declare `format()` as the template function for the best practice, which in my opinion defeats the purpose of introducing `format_context` in the first place.

Also, since `fmt-iter-for<charT>` is unspecified, if it is specified in some library implementation as the same type as `format_context`'s `Out` parameters, then `color` will suddenly become `formattable`. This lack of guarantee about `formattable` can bring unnecessary confusion.

I think we should ensure that `color` is `formattable`, because it is formattable.

[2025-06-12; Reflector poll]

Set priority to 2 after reflector poll.

"This change would prevent future evolution of the API." "Maybe `formatter` is not useful and `formattable_with` should always be used."

Proposed resolution:

This wording is relative to N5008.

1. Modify 28.5.6.3 [format.formattable] as indicated:

   > -1- Let `fmt-iter-for<charT>` be an unspecified type that models `output_iterator<const charT&>` (24.3.4.10 [iterator.concept.output]).

   > >     […]
   > >     template<class T, class charT>
   > >       concept formattable =
   > >         formattable-with<remove_reference_t<T>, conditional_t<same_as<charT, char>, format_context, wformat_context>basic_format_context<fmt-iter-for<charT>, charT>>;

### 4241(i). `ranges::for_each(_n)` should be less constrained

Section: 26.6.5 [alg.foreach] Status: LEWG Submitter: Jiang An Opened: 2025-04-08 Last modified: 2025-10-23

Priority: 3

View other active issues in [alg.foreach].

View all other issues in [alg.foreach].

View all issues with LEWG status.

Discussion:

Currently, `ranges::for_each(_n)` are constrained with `indirectly_unary_invocable`, which doesn't meet the actual use of the range elements. These algorithms are only expected to invoke the callable object with the possibly projected elements, and not to use any element as the value type. Moreover, `indirectly_unary_invocable` requires the callable object to be copy constructible, which might be undesired because the corresponding `std::for_each(_n)` only require move constructibilty.

LWG 4171(i) talked about the breakage around `ranges::for_each` introduced by P2609R3. P2609R3 looks like a reasonable fix as long as the affected algorithms potentially use the intermediate element values copied as `std::iter_value_t<I>`. However, when the algorithm is not expected to or even required not to do this, P2609R3 can bring unexpected impacts. It seems that constraints around `iter_value_t` should be avoided for such an algorithm.

[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]

This is design change. More algorithms (like `count_if`, `any_of`, etc.) that never produce `iter_value_t` are affected.

Example of affected code:

>     std::vector<std::string> v;
>     // the following not working, constraints not satisfied
>     std::ranges::for_each(
>       v | std::views::as_rvalue,
>       [](std::string&& s) { }
>     );

Proposed resolution:

This wording is relative to N5008.

1. Modify 26.4 [algorithm.syn], header `<algorithm>` synopsis, as indicated:

   >     […]
   >     namespace ranges {
   >       template<class I, class F>
   >       using for_each_result = in_fun_result<I, F>;

   >       template<input_iterator I, sentinel_for<I> S, class Proj = identity,
   >                indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
   >         requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
   >         constexpr for_each_result<I, Fun>
   >           for_each(I first, S last, Fun f, Proj proj = {});
   >       template<input_range R, class Proj = identity,
   >                indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun>
   >         requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>>
   >         constexpr for_each_result<borrowed_iterator_t<R>, Fun>
   >           for_each(R&& r, Fun f, Proj proj = {});
   >     }
   >     […]
   >     namespace ranges {
   >       template<class I, class F>
   >       using for_each_n_result = in_fun_result<I, F>;

   >       template<input_iterator I, class Proj = identity,
   >                indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
   >          requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
   >         constexpr for_each_n_result<I, Fun>
   >           for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
   >     }
   >     […]

2. Modify 26.6.5 [alg.foreach] as indicated:

   >     template<input_iterator I, sentinel_for<I> S, class Proj = identity,
   >              indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
   >       requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
   >       constexpr ranges::for_each_result<I, Fun>
   >         ranges::for_each(I first, S last, Fun f, Proj proj = {});
   >     template<input_range R, class Proj = identity,
   >              indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun>
   >       requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>>
   >       constexpr ranges::for_each_result<borrowed_iterator_t<R>, Fun>
   >         ranges::for_each(R&& r, Fun f, Proj proj = {});

   > > […]

   > > -15- [Note 6: The overloads in namespace `ranges` require `Fun` to model `copy_constructible`. — end note]

   > […]

   >     template<input_iterator I, class Proj = identity,
   >              indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
   >       requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
   >       constexpr ranges::for_each_n_result<I, Fun>
   >         ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});

   > > […]

   > > -30- [Note 11: The overload in namespace `ranges` requires `Fun` to model `copy_constructible`. — end note]

### 4250(i). `swap` overloads for `indirect` and `polymorphic` only found by ADL

Section: 20.4.1.7 [indirect.swap], 20.4.2.7 [polymorphic.swap] Status: LEWG Submitter: Jonathan Wakely Opened: 2025-05-01 Last modified: 2025-10-14

Priority: 2

View all issues with LEWG status.

Discussion:

The non-member `swap` overloads for `std::indirect` and `std::polymorphic` are defined as hidden friends, so are only available via ADL. This means that calling `std::swap(i1, i2)` will always use the generic `std::swap` instead of the custom overload for `std::indirect`.

[2025-10-14; Reflector poll]

Set priority to 2 after reflector poll. Status New → LEWG.

This is a broader issue than just `std::indirect` and `std::polymorphic`, it also affects `std::jthread`, `std::mdspan`, `move_only_function`, `copyable_function`, `node-handle`, `inplace_vector`, `flat_map` and co., and maybe more.

Are we comfortable that defining `swap` as a hidden friend means that the generic `std::swap` might be suboptimal, and for allocator-aware types might even do something with different effects? e.g. if `propagate_on_container_swap` is true but `propagate_on_container_move_assignment` is false.

Proposed resolution:

This wording is relative to N5008.

1. Modify 20.2.2 [memory.syn] as indicated:

   >     // 20.4.1, class template indirect
   >     template<class T, class Allocator = allocator<T>>
   >       class indirect;

   >     template<class T, class Allocator>
   >     constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(see below);

   >     // 20.4.1.10, hash support
   >     template<class T, class Alloc> struct hash<indirect<T, Alloc>>;

   >     // 20.4.2, class template polymorphic
   >     template<class T, class Allocator = allocator<T>>
   >       class polymorphic;

   >     template<class T, class Allocator>
   >     constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(see below);

2. Modify 20.4.1.2 [indirect.syn] as indicated:

   >     // 20.4.1.7, swap
   >     constexpr void swap(indirect& other) noexcept(see below);
   >     friend constexpr void swap(indirect& lhs, indirect& rhs) noexcept(see below);

3. Modify 20.4.1.7 [indirect.swap] as indicated:

   >     template<class T, class Allocator>
   >     constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));

   > > -3- Effects: Equivalent to `lhs.swap(rhs)`.

4. Modify 20.4.2.2 [polymorphic.syn] as indicated:

   >     // 20.4.2.7, swap
   >     constexpr void swap(polymorphic& other) noexcept(see below);
   >     friend constexpr void swap(polymorphic& lhs, polymorphic& rhs) noexcept(see below);

5. Modify 20.4.2.7 [polymorphic.swap] as indicated:

   >     template<class T, class Allocator>
   >     constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));

   > > -3- Effects: Equivalent to `lhs.swap(rhs)`.

### 4252(i). Are exposition-only classes considered specified for the purpose of `final`?

Section: 16.4.6.13 [derivation] Status: New Submitter: Jiang An Opened: 2025-04-28 Last modified: 2025-08-29

Priority: 3

View all other issues in [derivation].

View all issues with New status.

Discussion:

Currently, iterator and sentinel types of several views are exposition-only in the standard wording, and none of them is specified to be `final`. These types are arguably required to be non-final due to 16.4.6.13 [derivation] p4 because it's possible to say they are specified except for names.

However, libc++ marks `join_view`'s iterator `final` for some reasons (https://reviews.llvm.org/D142811#inline-1383022). Perhaps we should clarify that the `final`-ity of exposition-only class is unspecified.

[2025-08-29; Reflector poll]

Set priority to 3 after reflector poll. Votes for P2 (due to implementation divergence), NAD, and LEWG.

Proposed resolution:

This wording is relative to N5008.

1. Modify 16.4.6.13 [derivation] as indicated:

   > -4- All types specified in the C++ standard library shall be non-`final` types unless otherwise specified. Exposition-only classes (16.3.3.2 [expos.only.entity]) are not considered specified for the purpose of `final`.

### 4254(i). `stride_view::iterator` should provide `operator->`

Section: 25.7.32.3 [range.stride.iterator] Status: New Submitter: Hewill Kang Opened: 2025-05-01 Last modified: 2025-10-21

Priority: 3

View all issues with New status.

Discussion:

Currently, only `filter_view::iterator` and `join_view::InnerIter` in `<ranges>` provide `operator->`, which makes sense since their `operator*` simply dereferences the underlying iterator.

It turns out this is also true for `stride_view::iterator`, suggesting that providing `operator->` does is the intuitive thing to do, e.g. when wrapping pointers, keeping `operator->` valid so that users can continue to use `it->foo()`.

There is no reason to give up this convenience because `stride_view::iterator` is intended to preserve the nature of the underlying iterator.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N5008.

1. Modify 25.7.32.3 [range.stride.iterator] as indicated:

   >     namespace std::ranges {
   >       template<input_range V>
   >         requires view<V>
   >       template<bool Const>
   >       class stride_view<V>::iterator {
   >         using Parent = maybe-const<Const, stride_view>;                      // exposition only
   >         using Base = maybe-const<Const, V>;                                  // exposition only

   >         iterator_t<Base> current_ = iterator_t<Base>();                      // exposition only
   >         sentinel_t<Base> end_ = sentinel_t<Base>();                          // exposition only
   >         range_difference_t<Base> stride_ = 0;                                // exposition only
   >         range_difference_t<Base> missing_ = 0;                               // exposition only

   >         constexpr iterator(Parent* parent, iterator_t<Base> current,        // exposition only
   >                            range_difference_t<Base> missing = 0);
   >       public:
   >         […]

   >         constexpr decltype(auto) operator*() const { return *current_; }
   >         constexpr auto operator->() const
   >           requires has-arrow<iterator_t<Base>> && copyable<iterator_t<Base>>
   >         { return current_; }

   >         […]
   >       };
   >     }

### 4258(i). Size type mismatch in constraints involving Cpp17Allocator

Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2025-05-05 Last modified: 2025-10-14

Priority: 4

View other active issues in [allocator.requirements.general].

View all other issues in [allocator.requirements.general].

View all issues with New status.

Discussion:

It seems assumed that a `size_t` value can be passed to an allocator's `allocate` member function per the `simple-allocator` exposition-only concept in 16.4.4.6.1 [allocator.requirements.general] and the minimal constraints for allocator types in 23.2.2.2 [container.reqmts].

However, it is not exactly required that a type meeting the Cpp17Allocator requirements can be used with `size_t` values, because only `allocator_traits<A>::size_type`, which is possibly not `size_t`, is required to be usable with the allocator.

Do we want to change these constraints, or change the Cpp17Allocator requirements to require accepting `size_t` values?

[2025-10-14; Reflector poll]

Set priority to 4 after reflector poll.

"The wording technically allows you to write `allocate(same_as<size_type> auto)` and reject any other type."

"I imagine lots of code relies on `std::size_t` being accepted. That's surely a general design issue that's much broader than just the Cpp17Allocator requirement, and making the requirement stricter seems evolutionary."

Proposed resolution:

### 4261(i). P1206R7 broke uses of container adaptors with old custom sequence containers

Section: 27.4.3.8.2 [string.find] Status: New Submitter: Jiang An Opened: 2025-05-07 Last modified: 2025-10-17

Priority: 3

View all other issues in [string.find].

View all issues with New status.

Discussion:

P1206R7 added some requirements for sequence containers, including program-defined ones.

These requirements mean that if a custom C++20 sequence container type has no newly required member functions added in C++23 mode, it is no longer a sequence container, and uses of it with `std::stack`, `std::queue`, or `std::priority_queue` possibly have undefined behavior or make the program ill-formed. Additionally, some users want to use C++23 flat container adaptors with old sequence containers (llvm/llvm-project#136656), it is unclear whether such uses are intentionally rejected.

Perhaps the requirements on `from_range_t` constructor, `insert_range` and `assign_range` should be made optional, although these operations should have specified semantics whenever being well-formed in immediate contexts.

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

"We keep adding container requirements, cleaning up the adaptors might need a paper."

Proposed resolution:

### 4262(i). `copy_if`, `remove_copy`, `remove_copy_if`, `unique_copy` have too strong preconditions

Section: 26.7.1 [alg.copy], 26.7.8 [alg.remove], 26.7.9 [alg.unique] Status: New Submitter: Alex Guteniev Opened: 2025-05-12 Last modified: 2025-10-21

Priority: 3

View other active issues in [alg.copy].

View all other issues in [alg.copy].

View all issues with New status.

Discussion:

26.7.1 [alg.copy]/16 , 26.7.8 [alg.remove]/11, 26.7.9 [alg.unique]/8.1 all say:

> Preconditions: The ranges `[first, last)` and `[result, result + (last - first))` do not overlap.

These algorithms may produce fewer elements than `(last - first)`. If this is known in advance, a smaller output range may be used, so that the precondition will not be satisfied.

Example from LLVM test suite ( /libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp):

>     std::array<S, 4> in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}};
>     std::array<S, 2> out;
>     auto ret = std::ranges::copy_if(in.begin(), in.end(), out.begin(), [](int i) { return i == 3; }, &S::val);

I think there should be a weaker precondition, like 26.7.1 [alg.copy]/2:

> Preconditions: `result` is not in the range `[first, last)`.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

"The proposed resolution is incorrect, because the output iterator could ‘enter’ the range [first, last) via being incremented. If the output iterator is a reverse iterator, it starts writing to the end of [first,last) before we need to read from there."

"NAD - nothing is broken with the existing constraint. Relaxing it is a design change."

"NAD. The right behavior, if the algorithm runs out of output space, is to return the last processed input iterator and the "next spot" output iterator. This is what P3179 (parallel ranges algorithms) does with its range-as-output algorithms. I would not want the proposed resolution without this change. Otherwise, we would be introducing new UB unnecessarily."

Proposed resolution:

This wording is relative to N5008.

1. Modify 26.7.1 [alg.copy] as indicated:

   >     template<class InputIterator, class OutputIterator, class Predicate>
   >       constexpr OutputIterator copy_if(InputIterator first, InputIterator last,
   >                                        OutputIterator result, Predicate pred);
   >     […]
   >     template<input_range R, weakly_incrementable O, class Proj = identity,
   >              indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
   >       requires indirectly_copyable<iterator_t<R>, O>
   >       constexpr ranges::copy_if_result<borrowed_iterator_t<R>, O>
   >         ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {});

   > > -15- Let `E` be: […]

   > > -16 Preconditions: `result` is not in the range `[first, last)`The ranges `[first, last)` and `[result, result + (last - first))` do not overlap.

2. Modify 26.7.8 [alg.remove] as indicated:

   >     template<class InputIterator, class OutputIterator,
   >              class T = iterator_traits<InputIterator>::value_type>
   >       constexpr OutputIterator
   >         remove_copy(InputIterator first, InputIterator last,
   >                     OutputIterator result, const T& value);
   >     […]
   >     template<input_range R, weakly_incrementable O, class Proj = identity,
   >              indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
   >       requires indirectly_copyable<iterator_t<R>, O>
   >       constexpr ranges::remove_copy_if_result<borrowed_iterator_t<R>, O>
   >         ranges::remove_copy_if(R&& r, O result, Pred pred, Proj proj = {});

   > > -8- Let `E` be […]

   > > […]

   > > -11 Preconditions: `result` is not in the range `[first, last)`The ranges `[first, last)` and `[result, result + (last - first))` do not overlap.

3. Modify 26.7.9 [alg.unique] as indicated:

   >     template<class InputIterator, class OutputIterator>
   >       constexpr OutputIterator
   >         unique_copy(InputIterator first, InputIterator last,
   >                     OutputIterator result);
   >     […]
   >     template<input_range R, weakly_incrementable O, class Proj = identity,
   >              indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
   >       requires indirectly_copyable<iterator_t<R>, O> &&
   >               (forward_iterator<iterator_t<R>> ||
   >               (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>) ||
   >               indirectly_copyable_storable<iterator_t<R>, O>)
   >       constexpr ranges::unique_copy_result<borrowed_iterator_t<R>, O>
   >         ranges::unique_copy(R&& r, O result, C comp = {}, Proj proj = {});

   > > -6- Let `pred` be […]

   > > -7- Mandates: […]

   > > -8 Preconditions:

   > > 1. (8.1) — `result` is not in the range `[first, last)`The ranges `[first, last)` and `[result, result + (last - first))` do not overlap.

   > > 2. (8.2) — […]

### 4263(i). What should `std::format_to` etc. behave when the output is overlong?

Section: 28.5 [format] Status: New Submitter: Jiang An Opened: 2025-05-14 Last modified: 2025-10-17

Priority: 3

View other active issues in [format].

View all other issues in [format].

View all issues with New status.

Discussion:

It seems permitted to write overly long contents via an (C++20) output iterator with `std::format_to(_n)`, where the length isn't representable in `size_t` or `ptrdiff_t`, especially when the implementation provides integer-class types. However, currently some implementation can't properly handle content whose length is greater than `PTRDIFF_MAX`. Presumably we don't want UB in such cases. Should we explicitly allow throwing exception and/or silent truncation?

Additionally, given that the return type of `std::formatted_size` is `std::size_t`, even if the implementation supports formatted contents whose lengths are greater than `SIZE_MAX`, the length can't be correctly returned. Perhaps we need to either precisely specify the return value as modulo arithmetic seems undesired, or specify that an exception is thrown.

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

Victor thinks it should be an exception.

Proposed resolution:

### 4268(i). `function<void()>` suppresses `nodiscard` warnings

Section: 22.10.4 [func.require] Status: New Submitter: Jonathan Wakely Opened: 2025-05-29 Last modified: 2025-10-21

Priority: 3

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

    struct [[nodiscard]] A { };
    A f();
    std::function<void()> func = f;

Invoking `func()` will discard the return value of `f()`, but there will be no warning. This is because `INVOKE<void>(...)` is defined in terms of `static_cast<void>(...)` and the explicit cast to void suppresses `nodiscard` warnings. This is in contast to `INVOKE<R>(...)` where the conversion to non-void `R` is implicit.

It seems right that `std::invoke_r<void>(f)` should not give `nodiscard` warnings, because that's quite explicit about converting to void, and similarly for `std::bind<void>(f)()`. However, I think it's debatable whether all uses of `INVOKE<void>` (and `std::function<void()>` in particular) intend an explicit cast to void that ignores `nodiscard` types. It's very easy to set `f` as the target of `func` and then lose its warning, and there's no explicit use of `void` when you write `func = f; func();`.

We could consider defining `INVOKE<void>(...)` to be an expression of type void, without explicitly saying there's a cast to void. For example, `(INVOKE(...), void())` would invoke the invocable and have type `void`, but would not require any `nodiscard` warnings to be suppressed. If we did that, some uses of `INVOKE<R>` such as `std::invoke_r` and `std::bind<R>` might need to be adjusted to preserve the explicit conversion to void. That would allow us to be selective about which uses of `INVOKE<void>` we consider to be explicit about discarding results, and which we don't.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Split opinions between considering this NAD due warnings being QoI matter, and explicit use `static_cast<void>` indicating that any `nodiscard` warning should be silenced.

Concerns where expressed that removing `static_assert` is insufficient, as warnings may be still suppressed by heuristics that remove warnings originating from system headers.

Proposed resolution:

### 4270(i). Diagnose misuse of `std::projected::operator*`

Section: 24.3.6.4 [projected] Status: New Submitter: Jiang An Opened: 2025-05-30 Last modified: 2025-10-20

Priority: 3

View all other issues in [projected].

View all issues with New status.

Discussion:

`std::projected::operator*` is only intentionally used for type calculation like `std::declval`. Currently, if one attempts to call it in a potentially evaluated expression, the program is ill-formed, no diagnostic required because the `operator*` is not defined.

It seems better to diagnose such misuse, as we are already diagnosing misuse of `std::declval` (22.2.6 [declval]/2). Implementation used to perform such misuse via `std::ranges::iter_move`, but libc++ and libstdc++ have already switched not to do this (see microsoft/STL#5555).

[2025-10-20; Reflector poll.]

Set priority to 3 after reflector poll.

"NAD, the standard is clear that it can't be used."

Proposed resolution:

This wording is relative to N5008.

1. Modify 24.3.6.4 [projected] as indicated:

   >     namespace std {
   >       template<class I, class Proj>
   >       struct projected-impl { // exposition only
   >         struct type { // exposition only
   >           […]
   >           // models weakly_incrementable
   >           indirect_result_t<Proj&, I> operator*() const; // not defined
   >         };
   >       };
   >       […]
   >     }

   >     indirect_result_t<Proj&, I> operator*() const;

   > > -?- Mandates: This function is not odr-used (6.3 [basic.def.odr]).

### 4273(i). Standard execution policy types should be conventional tag class types

Section: 26.3.6 [execpol] Status: New Submitter: Jiang An Opened: 2025-06-03 Last modified: 2025-10-21

Priority: 3

View all issues with New status.

Discussion:

Existing standard execution policy types don't hold any state, and mostly act as disambiguating tags in parallel algorithms. Perhaps it will be better to ensure them to be similar to construction tag types, which possibly enables more desired usages.

Currently, libc++ makes these types non-movable, which is arguable undesired per the decision made in P3136R1.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

"Not seeing the point of this change. For code dealing with generic policies, it shouldn't be constructing new objects of the policy type (there could be implementation-defined policies, or ones added in future standards, which are not default-constructible). For code using a specific policy, just using the named variable is strictly less typing than constructing the type. Maybe NAD or LEWG." "Making them tag types helps e.g. P3481 where we have to store execution policies, and in P2500." "Don't you want copy constructible, not default constructible?"

Proposed resolution:

This wording is relative to N5008.

1. Modify 26.3.6.3 [execpol.seq] as indicated:

   >     class execution::sequenced_policy { unspecified
   >     public:
   >       explicit sequenced_policy() = default;
   >     };

2. Modify 26.3.6.4 [execpol.par] as indicated:

   >     class execution::parallel_policy { unspecified
   >     public:
   >       explicit parallel_policy() = default;
   >     };

3. Modify 26.3.6.5 [execpol.parunseq] as indicated:

   >     class execution::parallel_unsequenced_policy { unspecified
   >     public:
   >       explicit parallel_unsequenced_policy() = default;
   >     };

4. Modify 26.3.6.6 [execpol.unseq] as indicated:

   >     class execution::unsequenced_policy { unspecified
   >     public:
   >       explicit unsequenced_policy() = default;
   >     };

5. Modify 26.3.6.7 [execpol.objects] as indicated:

   >     inline constexpr execution::sequenced_policy execution::seq{ unspecified };
   >     inline constexpr execution::parallel_policy execution::par{ unspecified };
   >     inline constexpr execution::parallel_unsequenced_policy execution::par_unseq{ unspecified };
   >     inline constexpr execution::unsequenced_policy execution::unseq{ unspecified };

### 4277(i). §[algorithms.requirements] It is unclear what an algorithm is

Section: 26.2 [algorithms.requirements] Status: New Submitter: Jan Schultke Opened: 2025-06-08 Last modified: 2025-10-21

Priority: 4

View other active issues in [algorithms.requirements].

View all other issues in [algorithms.requirements].

View all issues with New status.

Discussion:

26.2 [algorithms.requirements] paragraph 15 states:

> The well-formedness and behavior of a call to an algorithm with an explicitly-specified template argument list is unspecified, except where explicitly stated otherwise.

It is unclear what this applies to because "algorithm" is never defined. 26.2 [algorithms.requirements] paragraph 1 uses the term while mentioning iterators and containers, but [algorithm] specifies function templates such as `std::gcd` which do not accept iterators.

The Remarks specifications in 26.8.9 [alg.min.max] providing exemptions from 26.2 [algorithms.requirements] paragraph 15 suggest that even function templates not accepting iterators are considered algorithms.

[2025-10-21; Reflector poll.]

Set priority to 4 after reflector poll.

"NAD, I don't think people are confused about what an algorithm is."

"I'd rather the fix would define 'algorithm', as even the Note below the change uses that term."

Proposed resolution:

This wording is relative to N5008.

1. Modify 26.2 [algorithms.requirements] as indicated:

   > -15- The well-formedness and behavior of a call to an algorithma function template specified in Clause 26 [algorithms] (including call operators of algorithm function objects denoted as algorithms throughout Clause 26.2 [algorithms.requirements]) with an explicitly-specified template argument list is unspecified, except where explicitly stated otherwise.

   > [Note 3: Consequently, an implementation can declare an algorithm with different template parameters than those presented. — end note]

### 4279(i). §[fs.op.absolute] Non-normative encouragement should be Recommended practice

Section: 31.12.13.2 [fs.op.absolute] Status: New Submitter: Jonathan Wakely Opened: 2025-06-16 Last modified: 2025-10-14

Priority: 4

View all issues with New status.

Discussion:

[fs.op.absolute] has a note giving strong encouragement, which should be normative:

> [Note 3: Implementations are strongly encouraged to not query secondary storage, and not consider `!exists(p)` an error. — end note]

The part about `!exists(p)` not being an error could definitely be a Recommended practice paragraph. Referring to "secondary storage" might need to be phrased differently to be normative (maybe with a note clarifying that the normative wording is referring to "secondary storage").

[2025-10-14; Reflector poll]

Set priority to 4 after reflector poll.

Could the `!exists(p)` part be implementation-defined?

Proposed resolution:

### 4281(i). Inconsistency between `value_or()` and `error_or()` in `std::expected`

Section: 22.5.3.7 [optional.observe], 22.8.6.6 [expected.object.obs] Status: New Submitter: Hiroaki Ando Opened: 2025-06-27 Last modified: 2025-10-16

Priority: 3

View other active issues in [optional.observe].

View all other issues in [optional.observe].

View all issues with New status.

Discussion:

In 22.8.6.6 [expected.object.obs]/19, the return value of `value_or()` is specified as follows:

> Returns: `has_value() ? **this : static_cast<T>(std::forward<U>(v))`.

Meanwhile, the return value of `error_or()` is specified as follows (22.8.6.6 [expected.object.obs]/23):

> Returns: `std::forward<G>(e)` if `has_value()` is `true`, `error()` otherwise.

Since these functions appear to be dual in nature, it would be preferable to maintain consistent notation.

Jonathan adds: The wording in `expected::error_or` is newer, having been added by P2505R5, and intentionally avoided a conditional expression (the problems with conditional expressions explained in P3177R0 don't actually affect these member functions, due to the non-const prvalue return type, but determining that there are no pessimized copies in `value_or` wouldn't be necessary if we didn't specify it with a conditional expression). The `error_or` wording also avoids using an explicit conversion when the Mandates: element requires implicit conversion to work anyway. We might want to rephrase the `value_or` wording to match `error_or`, or possibly make `value_or` and `error_or` even more explicit, specifying them in terms of `if`-`else`: :

> Effects: Equivalent to:

>     if (has_value())
>       return **this;
>     else
>       return std::forward<U>(v);

[2025-10-15; would be resolved by LWG 4406(i)]

[2025-10-16; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4284(i). Integer-class types should be formattable

Section: 28.5.6.4 [format.formatter.spec] Status: LEWG Submitter: Jiang An Opened: 2025-06-27 Last modified: 2025-10-21

Priority: 3

View other active issues in [format.formatter.spec].

View all other issues in [format.formatter.spec].

View all issues with LEWG status.

Discussion:

The following program doesn't compile with MSVC STL (Godbolt link).

>     #include <format>
>     #include <ranges>

>     int main() {
>       auto iv1 = std::views::iota(42ull, 1729ull);
>       auto iv2 = std::views::iota(iv1.begin(), iv1.end());
>       std::format("{}", iv2.size()); // Error
>     }

In MSVC STL, the type of `iv2.size()` is an integer-class type, and the standard hasn't guaranteed there're enabled formatter specializations for integer-class types. As a result, this program is not guaranteed to be well-formed.

Perhaps we should require enabled `std::formatter` specializations for integer-class types to get rid of such uncertainty and inconsistency.
Previous resolution [SUPERSEDED]

This wording is relative to N5008.

1. Modify 28.5.6.4 [format.formatter.spec] as indicated:

   > -2- […] Each header that declares the template `formatter` provides the following enabled specializations:

   > 1. (2.1) — […]

   > 2. (2.2) — […]

   > 3. (2.3) — […]

   > 4. (2.?) — For each `charT`, for each cv-unqualified integer-class type (24.3.4.4 [iterator.concept.winc]) `IntegerClass`, a specialization

   >    >     template<> struct formatter<IntegerClass, charT>;

   > 5. (2.4) — […]

[2025-09-18; Jiang An improves wording]

[2025-10-21; Reflector poll. Status changed: NEW → LEWG.]

Set priority to 3 after reflector poll. Send to LEWG.

"Which additional operations integer-class types should be required to support is a design question. Why `format` but not iostreams? What about `to_chars`/`from_chars`? All the stuff in `<bit>`? `<utility>`? `<cmath>`? You can show examples using any of these and claim 'uncertainty and inconsistency'."

Proposed resolution:

This wording is relative to N5014.

1. Modify 28.5.6.4 [format.formatter.spec] as indicated:

   > -2- […] Each header that declares the template `formatter` provides the following enabled specializations:

   > 1. (2.1) — […]

   > 2. (2.2) — […]

   > 3. (2.3) — […]

   > 4. (2.?) — For each `charT`, for each cv-unqualified integer-class type (24.3.4.4 [iterator.concept.winc]) `IntegerClass`, a specialization

   >    >     template<> struct formatter<IntegerClass, charT>;

   > 5. (2.4) — […]

2. Modify 28.5.2.2 [format.string.std] as indicated:

   > -20- The meaning of some non-string presentation types is defined in terms of a call to `to_chars`. In such cases, let `[first, last)` be a range large enough to hold the `to_chars` output and `value` be the formatting argument value. If the formatting argument is of an integer-class type (24.3.4.4 [iterator.concept.winc]), the formatting argument value is as-if converted to a hypothetical extended integer type of the same signedness and width as the integer-class type first. Formatting is done as if by calling `to_chars` as specified and copying the output through the output iterator of the format context.

3. Modify [tab:format.type.int] (Table 107) as indicated:

   > Table 107 — Meaning of type options for integer and integer-class types [tab:format.type.int]

### 4285(i). `time_get::do_get_date` is problematic even after LWG 461

Section: 28.3.4.6.2.2 [locale.time.get.members] Status: New Submitter: S. B. Tam Opened: 2025-06-27 Last modified: 2025-10-21

Priority: 3

View all issues with New status.

Discussion:

Background: https://github.com/cplusplus/draft/pull/8009

LWG 461(i) changed `time_get::do_get_date` to parse a set of fixed formats. For example, if `time_get::date_order()` is `no_order` or `mdy`, the format parsed by `do_get_date` is `%m/%d/%y`.

This has several problems:

1. When the resolution of LWG 461(i) was applied to the draft standard, the slashes were lost. This could mislead people into implementing `do_get_date` using the incorrect formats. Fortunately, none of the standard library implementations are affected by this mistake.

2. Only 2-digit years are accepted due to the use of `%y`. This could lead to incorrect parse if `strftime` uses `%Y` for the locale's date format.

3. `date_order()` might need additional locale facets to find out the date order, but it doesn't have an `ios_base&` argument from which to get the locale.

4. Many locales do not use any of the specified formats. For example, `de_DE` uses `%d.%m.%Y`, `zh_CN` uses `%Y年%m月%d日`. Although 28.3.4.6.2.3 [locale.time.get.virtuals]/5 gives an implementation the latitude to accept additional formats, ambiguity could arise if the locale's format disagrees with `date_order()`.

On POSIX systems, it is possible to query the locale's date format using `nl_langinfo(D_FMT)`. Maybe an implementation should be allowed to use that format instead of the one indicated by `date_order()`.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

"The current wording of `do_date_order()` suggests it should return one of the `dmy`, `mdy`, `ymd`, `ymd`, or `ydm` enumerators as long as the date format specified by `%x` has no components except day, month, and year. But that fails to consider alternative separators between the components. The `kok_IN` locale uses `"%d-%m-%y" which does not contain "other variable components" but still doesn't match any of the fixed formats in Table 102. We need normative wording allowing `no_order`, instead of just a footnoote, and we need to say `do_get_date` only has to use the formats in the table when `do_date_order() != no_order`. It should not default to the `mdy` format when `do_date_order()` returns `no_order`, because if the `mdy` format is correct then it should have returned `mdy`!"

[2025-10-21; Jonathan adds wording]

This restores the slashes lost by LWG 461(i) and attempts to make the use and effects of `no_order` more useful. It does not attempt to address the problem that `do_date_order()` has no access to an `ios_base` object.

Proposed resolution:

This wording is relative to N5014.

1. Modify 28.3.4.6.2.3 [locale.time.get.virtuals] as indicated:

   >     dateorder do_date_order() const;

   > > -1- Returns: An enumeration value indicating the preferred order of components for those date formats that are composed of day, month, and year.228 Returns `no_order` if the date format specified by `'x'` contains other variable components (e.g., Julian day, week number, week day) does not match one of the formats in Table [tab:locale.time.get.dogetdate] .

   > > > 228) This function is intended as a convenience only, for common formats, and can return `no_order` in valid locales.

   >     iter_type do_get_time(iter_type s, iter_type end, ios_base& str,
   >                           ios_base::iostate& err, tm* t) const;

   > > [...]

   >     iter_type do_get_date(iter_type s, iter_type end, ios_base& str,
   >                           ios_base::iostate& err, tm* t) const;

   > > -4- Effects: Reads characters starting at s until it has extracted those `tm` members and remaining format characters used by `time_put<>::put` to produce one of the following formats, or until it encounters an error. The format depends on the value returned by `date_order()` as shown in Table 102. When `do_date_order()` returns `no_order`, it is unspecified whether the format shown in the table is used, or whether additional implementation-defined formats are accepted.

   > > [Note ?: For example, an implementation can accept dates in the format specified by `'x'` as well as, or instead of, the format `"%m/%d/%y"`. — end note

   > > -5- An implementation may also accept additional implementation-defined formats.

   > > -6- Returns: An iterator pointing immediately beyond the last character recognized as possibly part of a valid date.

   > Table 102 — `do_get_date` effects [tab:locale.time.get.dogetdate]

   > `date_order()` | Format
   > --- | ---
   > `no_order` | `"%m/%d/%y"`
   > `dmy` | `"%d/%m/%y"`
   > `mdy` | `"%m/%d/%y"`
   > `ymd` | `"%y/%m/%d"`
   > `ydm` | `"%y/%d/%m"`

### 4287(i). §[locale.codecvt.virtuals] `do_in` and `do_out` could do with better specification

Section: 28.3.4.2.5.3 [locale.codecvt.virtuals] Status: New Submitter: S. B. Tam Opened: 2025-06-18 Last modified: 2025-10-17

Priority: 3

View other active issues in [locale.codecvt.virtuals].

View all other issues in [locale.codecvt.virtuals].

View all issues with New status.

Discussion:

Background: https://github.com/cplusplus/draft/pull/7347

The specification of `codecvt::do_in` and `codecvt::do_out` is unclear, and possibly incorrect:

1. the meaning of `noconv` is specified twice (once in paragraph 3, once in Table 91 [tab:locale.codecvt.inout]);

2. the effect on `from_next` is not specified;

3. the specification talks about "the input sequence [from, from_next)", but `from_next` is supposed to be an out parameter. I think it should say "[from, from_end)" instead.

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

"first element of the input that was not converted" doesn't make sense if there is no such element; similarly for the "first unchanged element".

Proposed resolution:

This wording is relative to N5008.

> [Drafting note: This is modified from Jonathan Wakely's suggestion in https://github.com/cplusplus/draft/pull/7347#issuecomment]

1. In 28.3.4.2.5.3 [locale.codecvt.virtuals] remove Table 91 [tab:locale.codecvt.inout] in its entirety:

   > Table 91 — `do_in`/`do_out` result values [tab:locale.codecvt.inout]
   > Value | Meaning
   > --- | ---
   > `ok` | completed the conversion
   > `partial` | not all source characters converted
   > `error` | encountered a character in `[from, from_end)` that cannot be converted
   > `noconv` | `internT` and `externT` are the same type, and input sequence is identical to converted sequence

2. Modify 28.3.4.2.5.3 [locale.codecvt.virtuals] as indicated:

   >     result do_out(
   >       stateT& state,
   >       const internT* from, const internT* from_end, const internT*& from_next,
   >       externT* to, externT* to_end, externT*& to_next) const;

   >     result do_in(
   >       stateT& state,
   >       const externT* from, const externT* from_end, const externT*& from_next,
   >       internT* to, internT* to_end, internT*& to_next) const;

   > > -1- Preconditions: […]

   > > -2- Effects: Translates characters in the source range `[from, from_end)`, placing the results in sequential positions starting at destination to. Converts no more than `(from_end - from)` source elements, and stores no more than `(to_end - to)` destination elements.

   > > -3- Stops if it encounters a character it cannot convert. It always leaves the `from_next` and `to_next` pointers pointing one beyond the last element successfully converted. If it returns `noconv`, `internT` and `externT` are the same type, and the converted sequence is identical to the input sequence `[from, from_next)`, `to_next` is set equal to `to`, the value of `state` is unchanged, and there are no changes to the values in `[to, to_end)`. If `internT` and `externT` are the same type and the converted sequence would be identical to the input sequence [`from`, `from_next`), then no elements are converted, the value of `state` is unchanged, there are no changes to the values in [`to`, `to_end`), and the result is `noconv`. Otherwise, if a character in [`from`,`from_end`) cannot be converted, conversion stops at that character and the result is `error`. Otherwise, if all input characters are successfully converted and placed in the output range, the result is `ok`. Otherwise, the result is `partial`. In all cases, `from_next` is set to point to the first element of the input that was not converted, `to_next` is set to point to the first unchanged element in the output. [Note: When the result is `noconv`, `from_next` points to `from` and `to_next` points to `to`. — end note]

   > > -4- A `codecvt` facet that is used by `basic_filebuf` […]

   > > -5- Returns: An enumeration value, as summarized in Table 91 The result as described above.

### 4288(i). The Constraints: element in [time.clock.system.members] is probably wrong

Section: 30.7.2.2 [time.clock.system.members] Status: New Submitter: Jiang An Opened: 2025-06-19 Last modified: 2025-08-26

Priority: 3

View all issues with New status.

Discussion:

Currently, the Constraints: element in 30.7.2.2 [time.clock.system.members] for the member typedef `system_clock::rep` imposes a requirement for the implementation, without establishing any condition for user code. Perhaps it's wrong to use a Constraints: element there.

Previous resolution [SUPERSEDED]

This wording is relative to N5008.

1. Modify 30.7.2.2 [time.clock.system.members] as indicated:

   >     using system_clock::rep = unspecified;

   > > -1- Constraints: The implementation shall ensure that `system_clock::duration::min() < system_clock::duration::zero()` is `true`. [Note 1: This implies that `rep` is a signed type. — end note]

[2025-07-15; Reflector discussion]

The discussion revealed a preference to not insert the additional "The implementation shall ensure that", because its not really needed, since this is just a normal implementation requirement that falls out of the specification.

[2025-08-21; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N5008.

1. Modify 30.7.2.2 [time.clock.system.members] as indicated:

   >     using system_clock::rep = unspecified;

   > > -1- Constraints: `system_clock::duration::min() < system_clock::duration::zero()` is `true`. [Note 1: This implies that `rep` is a signed type. — end note]

### 4289(i). Seed sequence is overspecified

Section: 29.5.3.2 [rand.req.seedseq] Status: LEWG Submitter: Magnus Fromreide Opened: 2025-06-22 Last modified: 2025-08-29

Priority: 3

View all other issues in [rand.req.seedseq].

View all issues with LEWG status.

Discussion:

The requirements on a seed sequence are so strict that it is forbidden to initialize a random number generator directly from a hardware random number generator without lots of boiler plate and intermediary objects.

The main problem is that the seed sequence requirements state that a seed sequence must be "kind of copyable" and that calls to the copy must generate the same output as calls to the original.

If one checks the uses of seed sequences then nothing makes use of this capability so I propose to just drop it.

There has been a previous attempt to handle this issue using P0205 which preserved the old seed sequence and added a new concept that it used to seed generators. That effort stalled with the comment that it should be solved without the new `std::seed_adapter`. This DR sidesteps that whole issue by simply removing the unused requirements from the seed sequence concept.

I will admit that I am unsure about the deletion of Note 1 but since it only seems to attempt to motivate the copyability I opted to let it go along with the requirements.

[2025-08-29; Reflector poll]

Set priority to 3 after reflector poll. This is a design change, send to LEWG.

Proposed resolution:

This wording is relative to N5008.

1. Modify 29.5.3.2 [rand.req.seedseq] as indicated:

   > -1- A seed sequence is an object that consumes a sequence of integer-valued data and produces a requested number of unsigned integer values `i`, `0 = i < 232`, based on the consumed data.

   > [Note 1: Such an object provides a mechanism to avoid replication of streams of random variates. This can be useful, for example, in applications requiring large numbers of random number engines. — end note]

   > -2- A class `S` meets the requirements of a seed sequence if the expressions shown in Table 124 [tab:rand.req.seedseq] are valid and have the indicated semantics, and if `S` also meets all other requirements of 29.5.3.2 [rand.req.seedseq]. In Table 124 [tab:rand.req.seedseq] and throughout this subclause:

   > 1. (2.1) — `T` is the type named by `S`' s associated `result_type`;

   > 2. (2.2) — `q` is a value of type `S` and `r` is a value of type `S` or `const S`; and

   > 3. (2.3) — `ib` and `ie` are input iterators with an unsigned integer `value_type` of at least 32 bits;

   > 4. (2.4) — `rb` and `re` are mutable random access iterators with an unsigned integer `value_type` of at least 32 bits;.

   > 5. (2.5) — `ob` is an output iterator; and

   > 6. (2.6) — `il` is a value of type `initializer_list<T>`.

   > > Table 124 — Seed sequence requirements [tab:rand.req.seedseq]
   > > Expression | Return type | Pre/post-condition | Complexity
   > > --- | --- | --- | ---
   > > `S::result_type` | `T` | `T` is an unsigned integer type (6.9.2 [basic.fundamental]) of at least 32 bits. |
   > > `S()` | `` | Creates a seed sequence with the same initial state as all other default-constructed seed sequences of type `S`. | constant
   > > `S(ib,ie)` | `` | Creates a seed sequence having internal state that depends on some or all of the bits of the supplied sequence `[ib, ie)`. | 𝒪(ie - ib)
   > > `S(il)` | `` | Same as `S(il.begin(), il.end())`. | same as `S(il.begin(), il.end())`
   > > `q.generate(rb,re)` | `void` | Does nothing if `rb == re`. Otherwise, fills the supplied sequence `[rb, re)` with 32-bit quantities that depend on the sequence supplied to the constructor and possibly also depend on the history of `generate`'s previous invocations. | 𝒪(re - rb)
   > > `r.size()` | `size_t` | The number of 32-bit units that would be copied by a call to `r.param`. | constant
   > > `r.param(ob)` | `void` | Copies to the given destination a sequence of 32-bit units that can be provided to the constructor of a second object of type `S`, and that would reproduce in that second object a state indistinguishable from the state of the first object. | 𝒪(r.size())

### 4295(i). [fund.ts.v3] `experimental::observer_ptr` should have more constexpr

Section: 8.2.6 [fund.ts.v3::memory.observer.ptr.special] Status: New Submitter: Jonathan Wakely Opened: 2025-07-14 Last modified: 2025-08-26

Priority: 4

View all issues with New status.

Discussion:

In the Library Fundamentals TS, the `swap` overload, `make_observer_ptr` function, and comparisons for `observer_ptr` could be constexpr, but are not. The member `swap` is already constexpr in the TS, and the non-member `swap` is constexpr in libc++ but not the comparisons. The proposed resolution has been implemented and tested in libstdc++.

If we ever rebase the TS on a new C++ standard (or put `observer_ptr` into the standard) the comparisons should all be updated like so:

>     template <class W1, class W2>
>     constexpr bool operator==(observer_ptr<W1> p1, observer_ptr<W2> p2);

> > Returns: `p1.get() == p2.get()`.

>     bool operator!=(...);

>     template <class W>
>     constexpr bool operator==(observer_ptr<W> p, nullptr_t) noexcept;
>     bool operator==(...);

> > Returns: `not p`.

>     bool operator!=(...);
>     bool operator!=(...);

>     template <class W1, class W2>
>     constexpr bool operator<=>(observer_ptr<W1> p1, observer_ptr<W2> p2);

> > Returns: `less<W3>compare_three_way()(p1.get(), p2.get())`, where `W3` is the composite pointer type (C++20 §7) of `W1*` and `W2*`.

>     bool operator>(...);
>     bool operator<=(...);
>     bool operator>=(...);

[2025-08-21; Reflector poll]

Set priority to 4 after reflector poll. Seven votes for P0 but one vote for NAD, so not made Tentatively Ready.

Proposed resolution:

This wording is relative to N4939.

1. Modify 8.1 [fund.ts.v3::memory.syn] as indicated:

   >     #include <memory>

   >     namespace std {
   >       namespace experimental::inline fundamentals_v3 {

   >         // 8.2, Non-owning (observer) pointers
   >         template <class W> class observer_ptr;

   >         // 8.2.6, observer_ptr specialized algorithms
   >         template <class W>
   >         constexpr void swap(observer_ptr<W>&, observer_ptr<W>&) noexcept;
   >         template <class W>
   >         constexpr observer_ptr<W> make_observer(W*) noexcept;
   >         // (in)equality operators
   >         template <class W1, class W2>
   >         constexpr bool operator==(observer_ptr<W1>, observer_ptr<W2>);

   >         template <class W1, class W2>
   >         constexpr bool operator!=(observer_ptr<W1>, observer_ptr<W2>);
   >         template <class W>
   >         constexpr bool operator==(observer_ptr<W>, nullptr_t) noexcept;
   >         template <class W>
   >         constexpr bool operator!=(observer_ptr<W>, nullptr_t) noexcept;
   >         template <class W>
   >         constexpr bool operator==(nullptr_t, observer_ptr<W>) noexcept;
   >         template <class W>
   >         constexpr bool operator!=(nullptr_t, observer_ptr<W>) noexcept;
   >         // ordering operators
   >         template <class W1, class W2>
   >         constexpr bool operator<(observer_ptr<W1>, observer_ptr<W2>);
   >         template <class W1, class W2>
   >         constexpr bool operator>(observer_ptr<W1>, observer_ptr<W2>);
   >         template <class W1, class W2>
   >         constexpr bool operator<=(observer_ptr<W1>, observer_ptr<W2>);
   >         template <class W1, class W2>
   >         constexpr bool operator>=(observer_ptr<W1>, observer_ptr<W2>);

   >       } // namespace experimental::inline fundamentals_v3

   >       // 8.2.7, observer_ptr hash support
   >       template <class T> struct hash;
   >       template <class T> struct hash<experimental::observer_ptr<T>>;

   >     } // namespace std

2. Modify 8.2.6 [fund.ts.v3::memory.observer.ptr.special] as indicated:

   >     template <class W>
   >       constexpr void swap(observer_ptr<W>& p1, observer_ptr<W>& p2) noexcept;

   > > -2- Effects: `p1.swap(p2)`.

   >     template <class W> constexpr observer_ptr<W> make_observer(W* p) noexcept;

   > > -4- Returns: `observer_ptr<W>{p}`.

   >     template <class W1, class W2>
   >       constexpr bool operator==(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -6- Returns: `p1.get() == p2.get()`.

   >     template <class W1, class W2>
   >       constexpr bool operator!=(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -8- Returns: `not (p1 == p2)`.

   >     template <class W>
   >       constexpr bool operator==(observer_ptr<W> p, nullptr_t) noexcept;
   >     template <class W>
   >       constexpr bool operator==(nullptr_t, observer_ptr<W> p) noexcept;

   > > -10- Returns: `not p`.

   >     template <class W>
   >       constexpr bool operator!=(observer_ptr<W> p, nullptr_t) noexcept;
   >     template <class W>
   >       constexpr bool operator!=(nullptr_t, observer_ptr<W> p) noexcept;

   > > -12- Returns: `(bool)p`.

   >     template <class W1, class W2>
   >       constexpr bool operator<(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -14- Returns: `less<W3>()(p1.get(), p2.get())`, where `W3` is the composite pointer type (C++20 §7) of `W1*` and `W2*`.

   >     template <class W1, class W2>
   >       constexpr bool operator>(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -16- Returns: `p2 < p1`.

   >     template <class W1, class W2>
   >       constexpr bool operator<=(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -16- Returns: `not (p2 < p1)`.

   >     template <class W1, class W2>
   >       constexpr bool operator>=(observer_ptr<W1> p1, observer_ptr<W2> p2);

   > > -16- Returns: `not (p1 < p2)`.

### 4296(i). Clarify that Cpp17Hash does not imply stateless

Section: 16.4.4.5 [hash.requirements] Status: New Submitter: Jonathan Wakely Opened: 2025-07-18 Last modified: 2025-10-14

Priority: 3

View all other issues in [hash.requirements].

View all issues with New status.

Discussion:

A colleague was mislead into thinking that the Cpp17Hash requirements imply hash functions must be stateless, so that every `h` would produce the same value for `h(k)`. The normative wording and the note can be interpreted as saying that the value of `h` is not relevant, only `k` matters.

> The value returned shall depend only on the argument `k` for the duration of the program.

> [Note 1: Thus all evaluations of the expression `h(k)` with the same value for `k` yield the same result for a given execution of the program. — end note]

I initially proposed changing it to "with the same values for `h` and `k` but Jens observed that if `h(k)` is allowed to change `h` then a subsequent call would not meet the condition "the same value of `h`". We should also clarify that the evaluation of `h(k)` should not change `h`, so that we have the same value of `h` each time.

Arguably, the wording in p2 "`h` is a value of type (possibly const) `H`" already covers this. It suggests to me that the effects in the table are for a particular value `h`, so for that value, the result of calling `h(k)` depends only on the value of `k` (and not on the current time, ambient temperature in Denmark, or the output of a random number generator). And the fact that `h` can be a value of type `const H` implies that calling `h(k)` doesn't change `h`.

Maybe we want to clarify it in terms of equality-preserving 18.2 [concepts.equality].

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Could say "Note: Thus, for a particular value `h`, all evaluations of the expression `h(k)` with the same value for `k` yield the same result for a given execution of the program."

Proposed resolution:

### 4298(i). §[meta.reflection.extract] Malformed "`F noexcept`" type

Section: 21.4.14 [meta.reflection.extract] Status: New Submitter: Jan Schultke Opened: 2025-07-10 Last modified: 2025-08-28

Priority: 3

View all issues with New status.

Discussion:

This is a follow-up from https://github.com/cplusplus/#8008

In 21.4.14 [meta.reflection.extract] paragraph 7, the type "`F noexcept`" is used, presumably with the intent that `noexcept` would be "injected" into the parameters-and-qualifiers of the declarator associated with `F`.

However, the syntax `F noexcept` is ill-formed, and does not have that intended effect. There ought to be a better way to phrase that.

[2025-08-27; Reflector poll]

Set priority to 3 after reflector poll.

"phrase this in terms of the quoted type description in 9.3.4.6 [dcl.fct] (see before bullet 1.6, The type of the declarator-id in D is...)."

Proposed resolution:

This wording is relative to N5014.

1. Modify 21.4.14 [meta.reflection.extract] as indicated:

   > [Drafting note: In bullet (7.1) we apply an additional drive-by fix for the broken "`T` is" construction before the `is_convertible_v` condition]

   >     template<class T>
   >       consteval T extract-member-or-function(info r); // exposition only

   > > -?- Given a function type `F`, let the type `F` with `noexcept` be the same type, except that the parameters-and-qualifiers in the noptr-declarator describing the type contain `noexcept(true)`.

   > > -6- Returns: […]

   > > -7- Throws: `meta::exception` unless

   > > 1. (7.1) — `r` represents a non-static data member with type `X`, that is not a bit-field, that is a direct member of class `C`, `T` and `C` are similar types (7.3.6 [conv.qual]), and `T` is `is_convertible_v<X C::*, T>` is `true`;

   > > 2. (7.2) — `r` represents an implicit object member function with type `F` or `F` with `noexcept` that is a direct member of a class `C`, and `T` is `F C::*`; or

   > > 3. (7.3) — `r` represents a non-member function, static member function, or explicit object member function of function type `F` or `F` with `noexcept`, and `T` is `F*`.

### 4303(i). `std::decay_t` in the specification of `ranges::distance` is problematic

Section: 24.4.4.3 [range.iter.op.distance] Status: New Submitter: Jiang An Opened: 2025-07-24 Last modified: 2025-08-29

Priority: 4

View all other issues in [range.iter.op.distance].

View all issues with New status.

Discussion:

This is discovered when implementing the resolution LWG 4242(i). Per LWG 4242(i), it is intended to allow `ranges::distance` to handle `volatile`-qualified iterator values. However, the uses of `decay_t` (established per LWG 3664(i)) are still problematic, because when `sized_sentinel_for<S, decay_t<I>>` is modeled, there's no semantic or syntactic requirement that `S` shall work with volatile-qualified `I`.

If we implement the constraint as is, there will still be some underconstrained cases. E.g. When the `operator==` or `operator-` intendedly rejects volatile-qualified iterators. And even when they accept volatile-qualified iterators, the additional semantic requirements imposed by `sized_sentinel_for<S, decay_t<I>>` are still undesired.

I think we should only decay arrays and keep `volatile` for non-array arguments.

[2025-08-29; Reflector poll]

Set priority to 4 after reflector poll.

Proposed resolution:

This wording is relative to N5014.

1. Modify 24.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     […]
   >     namespace std: {
   >       […]
   >       // 24.4.4.3 [range.iter.op.distance], ranges::distance

   >       template<class T>
   >         using distance-iterator-t =               // exposition only
   >           conditional_t<is_array_v<remove_reference_t<T>>,
   >             decay_t<T>, remove_const_t<remove_reference_t<T>>>;

   >       template<class I, sentinel_for<I> S>
   >         requires (!sized_sentinel_for<S, I>)
   >         constexpr iter_difference_t<I> distance(I first, S last); // freestanding
   >       template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S>
   >         constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last); // freestanding
   >       template<range R>
   >         constexpr range_difference_t<R> distance(R&& r); // freestanding
   >       […]
   >     }

2. Modify 24.4.4.3 [range.iter.op.distance] as indicated:

   >     template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S>
   >       constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last);

   > > -3- Effects: Equivalent to:

   > > >     if constexpr (!is_array_v<remove_reference_t<I>>)
   > > >       return last - first;
   > > >     else
   > > >       return last - static_cast<decay_t<I>>(first);

### 4306(i). Interaction between LWG 2259 and Constraints of member functions

Section: 16.4.6.5 [member.functions] Status: New Submitter: Jiang An Opened: 2025-07-24 Last modified: 2025-08-29

Priority: 3

View other active issues in [member.functions].

View all other issues in [member.functions].

View all issues with New status.

Discussion:

16.4.6.5 [member.functions]/2 seems to allow that even when the constraints are not met, such a default constructor can be selected, because no restriction is imposed when no overload is originally selected. Per the discussion in LWG 2563(i) (closed as NAD), it even seems to allow the implementation to add a default constructor when there's originally none.

However, we're constraining some member functions, e.g. default constructors of `unique_ptr`, `tuple`, and `variant`. Allowance of more permissive overload sets of constructors effectively renders the Constraints meaningless, because even if the implementation doesn't constrain the default constructors at all, it can still satisfy the restriction in 16.4.6.5 [member.functions]/2 since LWG 2259(i).

Consider the following example:

>     struct S
>     {
>       S() = delete;
>     };

>     static_assert(std::is_default_constructible_v<std::tuple<S>>);
>     std::tuple<S> t;

Even though currently the default constructor of `std::tuple` is constrained (22.4.4.2 [tuple.cnstr]/6), 16.4.6.5 [member.functions]/2 seemingly allows the implementation to add another overload that's also a default constructor, constrained by "`is_default_constructible_v<Ti>` is `false` for at least one `i`", and then render the example well-formed or only error on actual instantiation of the "additional" default constructor. Note that such an additional overload effectively removes the Constraints element requirements.

As shown by the example, the allowance in 16.4.6.5 [member.functions]/2 can weaken or even invalidate Constraints of member functions.

So perhaps some wording change is necessary to guarantee the Constraints of member functions to be meaningful.

[2025-08-29; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4307(i). Make good use of `sized-random-access-range` and `bidirectional-common` in `<ranges>`

Section: 25.2 [ranges.syn], 25.4.6 [range.refinements], 25.5.3.1 [view.interface.general], 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview], 25.7.12.2 [range.drop.view], 25.7.14.3 [range.join.iterator], 25.7.15.2 [range.join.with.view], 25.7.15.3 [range.join.with.iterator], 25.7.20.2 [range.common.view], 25.7.25.2 [range.zip.view], 25.7.30.2 [range.slide.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2025-07-28 Last modified: 2025-10-23

Priority: 4

View other active issues in [ranges.syn].

View all other issues in [ranges.syn].

View all issues with New status.

Discussion:

P3179 introduces a new exposition-only concept, `sized-random-access-range`, into `<ranges>` to simplify the signature of parallel range algorithms. However, this also applies more broadly to `<ranges>`, as we often need to determine whether a range satisfies both `sized_range` and `random_access_range` in order to dispatch optimized branches.

In addition, we often need to determine whether a range satisfies both `common_range` and `bidirectional_range` to decide whether to provide backward traversal capabilities, which is expressed by the exposition-only concept `bidirectional-common` introduced in P2441. Unfortunately, this concept currently only applies to a single section.

It would be much simpler and more readable if both concepts were available throughout `<ranges>`, which would also allow newly introduced adapters or other library features to take advantage of them. Note that since some of these simplifications change the order in which the concepts are spelled, they may not be purely editorial.

[2025-10-23; Reflector poll.]

Set priority to 4 after reflector poll.

Use of exposition-only concepts is not necessary improving readability or produced error messages.

Proposed resolution:

This wording is relative to N5014.

1. Modify 25.2 [ranges.syn], header `<ranges>` synopsis, as indicated:

   >     // mostly freestanding
   >     #include <compare>              // see 17.12.1 [compare.syn]
   >     #include <initializer_list>     // see 17.11.2 [initializer.list.syn]
   >     #include <iterator>             // see 24.2 [iterator.synopsis]

   >     namespace std::ranges {
   >       […]
   >       template<class T>
   >         concept sized-random-access-range = see below;              // exposition only

   >       template<class T>
   >         concept common-bidirectional-range = see below;             // exposition only
   >       […]
   >     }

2. Modify 25.4.6 [range.refinements] as indicated:

   > […]

   > -8- The exposition-only concept `sized-random-access-range` specifies the requirements of a `range` type that is sized and allows random access to its elements.

   >     template<class T>
   >       concept sized-random-access-range =           // exposition only
   >         random_access_range<T> && sized_range<T>;

   > [Note 1: This concept constrains some parallel algorithm overloads; see [algorithms]. — end note]

   > -?- The exposition-only concept `common-bidirectional-range` specifies the requirements of a `range` type that is bidirectional and whose iterator and sentinel types are the same.

   >     template<class T>
   >       concept common-bidirectional-range =           // exposition only
   >         bidirectional_range<T> && common_range<T>;

3. Modify 25.5.3.1 [view.interface.general] as indicated:

   > -1- The class template `view_interface` is a helper for defining view-like types that offer a container-like interface. It is parameterized with the type that is derived from it.

   >     namespace std::ranges {
   >       template<class D>
   >         requires is_class_v<D> && same_as<D, remove_cv_t<D>>
   >       class view_interface {
   >       private:
   >         […]
   >       public:
   >         […]
   >         constexpr decltype(auto) back() requires bidirectional_range<D> && common_range<D>common-bidirectional-range<D>;
   >         constexpr decltype(auto) back() const
   >           requires bidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>;
   >         […]
   >       };
   >     }

4. Modify 25.5.3.2 [view.interface.members] as indicated:

   >     constexpr decltype(auto) back() requires bidirectional_range<D> && common_range<D>common-bidirectional-range<D>;
   >     constexpr decltype(auto) back() const
   >       requires bidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>;

   > > -3- Hardened preconditions: `!empty()` is `true`.

   > > -4- Effects: Equivalent to: `return *ranges::prev(ranges::end(derived()));`

5. Modify 25.7.10.1 [range.take.overview] as indicated:

   > -2- The name `views::take` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::take(E, F)` is ill-formed. Otherwise, the expression `views::take(E, F)` is expression-equivalent to:

   > 1. […]

   > 2. (2.2) — Otherwise, if `T` models `random_access_range` and `sized_range` `sized-random-access-range` and is a specialization of `span` (23.7.2.2 [views.span]), `basic_string_view` (27.3 [string.view]), or `ranges::subrange` (25.5.4 [range.subrange]), then `U(ranges::begin(E), ranges::begin(E) + std::min<D>(ranges::distance(E), F))`, except that `E` is evaluated only once, where `U` is a type determined as follows:

   >    […]

   > 3. (2.3) — otherwise, if `T` is a specialization of `iota_view` (25.6.4.2 [range.iota.view]) that models `random_access_range` and `sized_range` `sized-random-access-range`, then `iota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F)))`, except that `E` is evaluated only once.

6. Modify 25.7.12.1 [range.drop.overview] as indicated:

   > -2- The name `views::drop` denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let `E` and `F` be expressions, let `T` be `remove_cvref_t<decltype((E))>`, and let `D` be `range_difference_t<decltype((E))>`. If `decltype((F))` does not model `convertible_to<D>`, `views::drop(E, F)` is ill-formed. Otherwise, the expression `views::drop(E, F)` is expression-equivalent to:

   > 1. […]

   > 2. (2.2) — Otherwise, if `T` models `random_access_range` and `sized_range` `sized-random-access-range` and is

   >    […]

   > 3. (2.3) — Otherwise, if `T` is a specialization of `subrange` (25.5.4 [range.subrange]) that models `random_access_range` and `sized_range` `sized-random-access-range`, then `T(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E), to-unsigned-like(ranges::distance(E) - std::min<D>(ranges::distance(E), F)))`, except that `E` and `F` are each evaluated only once.

7. Modify 25.7.12.2 [range.drop.view] as indicated:

   >     namespace std::ranges {
   >       template<view V>
   >       class drop_view : public view_interface<drop_view<V>> {
   >       public:
   >         […]
   >         constexpr auto begin()
   >           requires (!(simple-view<V> &&
   >                       random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>));
   >         constexpr auto begin() const
   >           requires random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>;
   >         […]
   >       };
   >       […]
   >     }

   > […]

   >     constexpr auto begin()
   >       requires (!(simple-view<V> &&
   >                   random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>));
   >     constexpr auto begin() const
   >       requires random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>;

   > > -3- Returns: `ranges::next(ranges::begin(base_), count_, ranges::end(base_))`.

8. Modify 25.7.14.3 [range.join.iterator] as indicated:

   >     namespace std::ranges {
   >       template<input_range V>
   >         requires view<V> && input_range<range_reference_t<V>>
   >       template<bool Const>
   >       struct join_view<V>::iterator {
   >       private:
   >         […]
   >       public:
   >         […]
   >         constexpr iterator& operator--()
   >           requires ref-is-glvalue && bidirectional_range<Base> &&
   >                    bidirectional_range<range_reference_t<Base>> &&
   >                    common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;

   >         constexpr iterator operator--(int)
   >           requires ref-is-glvalue && bidirectional_range<Base> &&
   >                    bidirectional_range<range_reference_t<Base>> &&
   >                    common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;
   >         […]
   >       };
   >     }

   > -1- `iterator::iterator_concept` is defined as follows::

   > 1. (1.1) — If `ref-is-glvalue` is `true`, `Base` models `bidirectional_range`, and `range_reference_t<Base>` models both `bidirectional_range` and `common_range` `common-bidirectional-range`, then `iterator_concept` denotes `bidirectional_iterator_tag`.

   >    […]
   > […]

   >     constexpr iterator& operator--()
   >       requires ref-is-glvalue && bidirectional_range<Base> &&
   >                bidirectional_range<range_reference_t<Base>> &&
   >                common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;

   > > -16- Effects: Equivalent to:

   > > […]

   >     constexpr iterator operator--(int)
   >       requires ref-is-glvalue && bidirectional_range<Base> &&
   >                bidirectional_range<range_reference_t<Base>> &&
   >                common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;

   > > -17- Effects: Equivalent to:

   > > […]

9. Modify 25.7.15.2 [range.join.with.view] as indicated:

   >     namespace std::ranges {
   >       template<class R>
   >       concept bidirectional-common = bidirectional_range<R> && common_range<R>;    // exposition only
   >       […]
   >     }

10. Modify 25.7.15.3 [range.join.with.iterator] as indicated:

    >     namespace std::ranges {
    >       template<input_range V, forward_range Pattern>
    >         requires view<V> && input_range<range_reference_t<V>>
    >               && view<Pattern> && concatable<range_reference_t<V>, Pattern>
    >       template<bool Const>
    >       class join_with_view<V, Pattern>::iterator {
    >       private:
    >         […]
    >       public:
    >         […]
    >         constexpr iterator& operator--()
    >           requires ref-is-glvalue && bidirectional_range<Base> &&
    >                    bidirectional-commoncommon-bidirectional-range<InnerBase> && bidirectional-commoncommon-bidirectional-range<PatternBase>;
    >         constexpr iterator operator--(int)
    >           requires ref-is-glvalue && bidirectional_range<Base> &&
    >                    bidirectional-commoncommon-bidirectional-range<InnerBase> && bidirectional-commoncommon-bidirectional-range<PatternBase>;
    >         […]
    >       };
    >     }

    > -1- `iterator::iterator_concept` is defined as follows::

    > 1. (1.1) — If `ref-is-glvalue` is `true`, `Base` models `bidirectional_range`, and `InnerBase` and `PatternBase` each model `bidirectional-common` `common-bidirectional-range`, then `iterator_concept` denotes `bidirectional_iterator_tag`.

    >    […]
    > […]

    >     constexpr iterator& operator--()
    >       requires ref-is-glvalue && bidirectional_range<Base> &&
    >                bidirectional-commoncommon-bidirectional-range<InnerBase> && bidirectional-commoncommon-bidirectional-range<PatternBase>;

    > > -16- Effects: Equivalent to:

    > […]

    >     constexpr iterator operator--(int)
    >       requires ref-is-glvalue && bidirectional_range<Base> &&
    >                bidirectional-commoncommon-bidirectional-range<InnerBase> && bidirectional-commoncommon-bidirectional-range<PatternBase>;

    > > -17- Effects: Equivalent to:

    > > […]

11. Modify 25.7.20.2 [range.common.view] as indicated:

    >     namespace std::ranges {
    >       template<view V>
    >         requires (!common_range<V> && copyable<iterator_t<V>>)
    >       class common_view : public view_interface<common_view<V>> {
    >       private:
    >         V base_ = V();  // exposition only

    >       public:
    >         […]
    >         constexpr auto begin() requires (!simple-view<V>) {
    >           if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
    >             return ranges::begin(base_);
    >           else
    >             return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::begin(base_));
    >         }

    >         constexpr auto begin() const requires range<const V> {
    >           if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
    >             return ranges::begin(base_);
    >           else
    >             return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::begin(base_));
    >         }

    >         constexpr auto end() requires (!simple-view<V>) {
    >           if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
    >             return ranges::begin(base_) + ranges::distance(base_);
    >           else
    >             return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::end(base_));
    >         }

    >         constexpr auto end() const requires range<const V> {
    >           if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
    >             return ranges::begin(base_) + ranges::distance(base_);
    >           else
    >             return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::end(base_));
    >         }
    >         […]
    >       };
    >       […]
    >     }

12. Modify 25.7.25.2 [range.zip.view] as indicated:

    >     namespace std::ranges {
    >       template<class... Rs>
    >       concept zip-is-common =                             // exposition only
    >         (sizeof...(Rs) == 1 && (common_range<Rs> && ...)) ||
    >         (!(bidirectional_range<Rs> && ...) && (common_range<Rs> && ...)) ||
    >         ((random_access_range<Rs> && ...) && (sized_range<Rs> && ...)sized-random-access-range<Rs> && ...);
    >       […]
    >     }

13. Modify 25.7.30.2 [range.slide.view] as indicated:

    >     namespace std::ranges {
    >       template<class V>
    >       concept slide-caches-nothing = random_access_range<V> && sized_range<V>sized-random-access-range<V>;       // exposition only

    >       template<class V>
    >       concept slide-caches-last =                                            // exposition only
    >         !slide-caches-nothing<V> && bidirectional_range<V> && common_range<V>common-bidirectional-range<V>;

    >       […]
    >     }

14. Modify 25.7.33.2 [range.cartesian.view] as indicated:

    >     namespace std::ranges {
    >       template<bool Const, class First, class... Vs>
    >       concept cartesian-product-is-random-access =          // exposition only
    >         (random_access_range<maybe-const<Const, First>> && ... &&
    >           (random_access_range<maybe-const<Const, Vs>>
    >             && sized_range<maybe-const<Const, Vs>>)sized-random-access-range<maybe-const<Const, Vs>>);

    >       template<class R>
    >       concept cartesian-product-common-arg =                // exposition only
    >         common_range<R> || (sized_range<R> && random_access_range<R>)sized-random-access-range<R>;
    >       […]
    >     }

### 4310(i). Is "the completion of the invocation of the constructor" considered as an expression?

Section: 32.4.3.3 [thread.thread.constr], 32.4.3.6 [thread.thread.member] Status: Core Submitter: jim x Opened: 2025-08-06 Last modified: 2025-10-23

Priority: 4

View other active issues in [thread.thread.constr].

View all other issues in [thread.thread.constr].

View all issues with Core status.

Discussion:

32.4.3.3 [thread.thread.constr] p6 says

> The completion of the invocation of the constructor synchronizes with the beginning of the invocation of the copy of `f`.

The intended meaning is that "The completion of the invocation of the constructor" happens-before "the beginning of the invocation of the copy of `f`.", so we can infer other happen-before relationships based on this point.

However, according to the definition of "happens-before" (6.10.2.2 [intro.races] p7),

> An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if either

> 1. (7.1) — […]

> 2. (7.2) — A synchronizes with B, or

That means A and B are first required to be expressions because the evaluation is in terms of expressions.

However, "The completion of the invocation of the constructor" and "the beginning of the invocation of the copy of `f`" are not expressions.

The similar issue is 32.4.3.6 [thread.thread.member] p4

> The completion of the thread represented by `*this` synchronizes with (6.10.2 [intro.multithread]) the corresponding successful `join()` return.

[2025-10-23; Reflector poll; Status changed: New → Core and P4.]

P4 at best. Sentiment towards NAD.

Proposed resolution:

### 4311(i). Can `std::pmr::polymorphic_allocator::construct` just call `std::uninitialized_construct_using_allocator`?

Section: 20.5.3.3 [mem.poly.allocator.mem] Status: New Submitter: Jiang An Opened: 2025-08-06 Last modified: 2025-10-16

Priority: 3

View all other issues in [mem.poly.allocator.mem].

View all issues with New status.

Discussion:

This is closely related to LWG 3901(i) but only affects arguably pathological cases.

Uses-allocator construction for cv-qualified types needs to be well-formed, but it's questionable to require `polymorphic_allocator::construct` to support constructing objects via pointer to a cv-qualified type. LWG 3870(i) banned such placement construction for `std::construct_at`, which is depended by `uninitialized_construct_using_allocator`.

As a result, it seems non-conforming to just use `uninitialized_construct_using_allocator` in `polymorphic_allocator::construct` since C++20, because the latter is still required to handle cv-qualified target types. If the status quo is considered unintended and needed to be fixed, perhaps we can just respecify `polymorphic_allocator::construct` to use `uninitialized_construct_using_allocator`.

[2025-10-15; Reflector poll]

Set priority to 3 after reflector poll.

"What is the extent of the breakage? `pmr::polymorphic_allocator::new_object` uses `construct`, so does this break users of that function?"

We could modify `new_object` to allocate and construct `remove_cv_t<T>` instead of `T`, so that it could still use `construct`.

Proposed resolution:

This wording is relative to N5014.

> [Drafting note: The Mandates: element allows implementations not to add constraints even if `uninitialized_construct_using_allocator` is constrained in the future. The Throws: element is made redundant by "Effects: Equivalent to", and it's already wrong because the exception can be thrown by a conversion function.]

1. Modify 20.5.3.3 [mem.poly.allocator.mem] as indicated:

   >     template<class T, class... Args>
   >       void construct(T* p, Args&&... args);

   > > -14- Mandates: Uses-allocator construction of `T` with allocator `*this` (see 20.2.8.2 [allocator.uses.construction]) and constructor arguments `std::forward<Args>(args)...``uninitialized_construct_using_allocator(p, *this, std::forward<Args>(args)...)` is well-formed.

   > > -15- Effects: Constructs a `T` object in the storage whose address is represented by `p` by uses-allocator construction with allocator `*this` and constructor arguments `std::forward<Args>(args)...`.Equivalent to:

   > > >     uninitialized_construct_using_allocator(p, *this, std::forward<Args>(args)...);

   > > -16- Throws: Nothing unless the constructor for `T` throws.

### 4313(i). Uses-allocator construction of `pair` in `tuple`'s `allocator_arg_t` constructors

Section: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Jiang An Opened: 2025-08-07 Last modified: 2025-10-14

Priority: 3

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with New status.

Discussion:

Before P0591R4, only `scoped_allocator_adaptor::construct` and `polymorphic_allocator::construct` specially handled `pair` for the purpose of uses-allocator construction. The primary definition of uses-allocator construction (in e.g., N4659 [allocator.uses.construction]) did not specially handle `pair`. The `allocator_arg_t` constructors of `tuple`, which were specified to construct `tuple` elements with uses-allocator constructor (per e.g., N4659 [tuple.cnstr] p26), did not specially handle `pair` either.

P0591R4 redefined uses-allocator construction in terms of `make_obj_using_allocator` in [allocator.uses.construction] p1 as:

> When applied to the construction of an object of type `T`, it is equivalent to initializing it with the value of the expression `make_obj_using_allocator<T>(alloc, args...)`, described below.

And the new definition does handle `pair`. As the specification of `allocator_arg_t` constructors of `tuple` (now in 22.4.4.2 [tuple.cnstr] p33) still refer to uses-allocator construction as-is, these constructors should construct a `pair` element in a way equivalent to `make_obj_using_allocator` now.

The following example shows the behavioral difference.

>     #include <cstddef>
>     #include <utility>
>     #include <tuple>
>     #include <memory>
>     #include <vector>
>     #include <cassert>

>     template<class T>
>     class payload_ator {
>       int payload{};

>     public:
>       using value_type = T;

>       payload_ator() = default;

>       constexpr explicit payload_ator(int n) noexcept : payload{n} {}

>       template<class U>
>       constexpr explicit payload_ator(payload_ator<U> a) noexcept : payload{a.payload} {}

>       friend bool operator==(payload_ator, payload_ator) = default;

>       template<class U>
>       friend constexpr bool operator==(payload_ator x, payload_ator<U> y) noexcept {
>         return x.payload == y.payload;
>       }

>       constexpr T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }

>       constexpr void deallocate(T* p, std::size_t n) { return std::allocator<T>{}.deallocate(p, n); }

>       constexpr int get_payload() const noexcept { return payload; }
>     };

>     bool test() {
>       constexpr int in_v = 42;
>       using my_pair_t = std::pair<int, std::vector<int, payload_ator<int>>>;
>       std::tuple<my_pair_t> t(std::allocator_arg, payload_ator<int>{in_v});
>       auto out_v = std::get<0>(t).second.get_allocator().get_payload();
>       return in_v == out_v;
>     }

>     int main() {
>       assert(test()); // passes only if allocator_arg_t constructors of tuple specially handle pair
>     }

However, the behavioral changes of these constructors were not discussed in P0591R4, and existing implementations that claim full implementation of P0591R4 (MSVC STL and libstdc++) did not change these constructors (demo).

Given that implementations did not recognize changes of `allocator_arg_t` constructors as part of the paper, and special handling of `pair` would significantly complicate these constructors, perhaps we should explicitly specify that these constructors behave as if special handling for `pair` were missing.

[2025-08-17; Pablo comments]

I don't agree with it or the PR. It seems like the implementations are simply lagging behind. Since `make_obj_using_allocator` is in the standard, `tuple` is easy enough to implement simply by delegating to that function. I regard the failure to handle `pair` in a general way to be a defect in the C++11 specification and that handling it correctly, though technically a change of behavior, is more likely to fix bugs than to create them. It is exactly this scattershot restatement of uses-allocator construction for `pair` that I hoped to fix with P0591, even though I missed `tuple` in my exposition.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N5014.

1. Modify 22.4.4.2 [tuple.cnstr] as indicated:

   >     template<class Alloc>
   >       constexpr explicit(see below )
   >         tuple(allocator_arg_t, const Alloc& a);
   >     […]
   >     template<class Alloc, class U1, class U2>
   >       constexpr explicit(see below)
   >         tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
   >     template<class Alloc, class U1, class U2>
   >       constexpr explicit(see below)
   >         tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&);
   >     template<class Alloc, class U1, class U2>
   >       constexpr explicit(see below)
   >         tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);
   >     template<class Alloc, class U1, class U2>
   >       constexpr explicit(see below)
   >         tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
   >     template<class Alloc, tuple-like UTuple>
   >       constexpr explicit(see below)
   >       tuple(allocator_arg_t, const Alloc& a, UTuple&&);

   > > -32- Preconditions: `Alloc` meets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]).

   > > -33- Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction (20.2.8.2 [allocator.uses.construction]), except that the construction behaves as if there were only one `uses_allocator_construction_args` overload and the overload behaved the same as the first actual overload without Constraints.

### 4319(i). Supporting copy-elision in function wrappers

Section: 22.10.4 [func.require] Status: New Submitter: Tomasz Kamiński Opened: 2025-08-19 Last modified: 2025-10-14

Priority: 3

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

The wording for argument forwarding call wrappers in 22.10.4 [func.require] p3,

> This forwarding step delivers rvalue arguments as rvalue references and lvalue arguments as lvalue references.

requires that each wrapper binds a temporary to rvalue reference (materializing it), and then pass that xvalue. This essentially codifies an implementation where wrappers provide an `operator()` that accepts `Args&&...`. This is fine for most of the wrappers.

For some wrappers more efficient implementation strategies are possible:

- `bind_front(f)/bind_back(f)` without bound args could return a copy of `f`

- `bind_front<f>()/bind_back<f>()` could produce a `__function_wrapper<f>`, that for function pointers can be invoked using a surrogate function call.

However, such implementation strategies are currently disallowed per 22.10.4 [func.require] p3, as invoking the function wrapper with a prvalue `bind_front(f)(T())` requires a temporary to be materialized, and then moved into the parameter of `f`. For example:

>     struct M
>     {
>       M() { std::cout << "Default" < std::endl; }
>       M(M&& m) { std::cout << "Move" < std::endl; }
>     };

>     struct F
>     {
>       void operator()(M m) {}
>     } f;

The call `f(M{})` will print only "`Default`" but `bind_front(f)(M{})` is required to produce "`Default`" and "`Move`". We should allow implementations to elide the move operations, but not require it.

The suggested changes by this issue have been implemented in libstdc++ for `bind_front/bind_back`.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

The submitter prefers to withdraw the issue because the proposed resolution changes the result of overload resolution in unspecified ways. It would no longer be specified whether `bind_front(F{})({})` works or not, as it would depend whether `bind_front(F{})` returned a call wrapper or the `F` object without wrapping. It would also make the effects of `bind_front(S{})(0, 0)` unspecified if the call could resolve to either `S::operator()(long, long)` or `S::operator()(int, void*)`, depending on whether `bind_front(S{})` returns a call wrapper or the `S` object.

Proposed resolution:

This wording is relative to N5014.

1. Modify 22.10.4 [func.require] as indicated:

   > -3- Every call wrapper (22.10.3 [func.def]) meets the Cpp17MoveConstructible and Cpp17Destructible requirements. An argument forwarding call wrapper is a call wrapper that can be called with an arbitrary argument list and delivers the arguments to the target object as references. This forwarding step delivers rvalue arguments as rvalue references and lvalue arguments as lvalue references.:

   > 1. (3.?) — lvalue arguments as lvalues,

   > 2. (3.?) — xvalue arguments as xvalues,

   > 3. (3.?) — prvalue arguments as either prvalues or xvalues.

### 4320(i). `hive` operations involving insertion/erasure should have `𝒪(log n)` time complexity

Section: 23.3.9 [hive] Status: New Submitter: Matt Bentley Opened: 2025-08-19 Last modified: 2026-02-18

Priority: 3

View all issues with New status.

Discussion:

Under 23.3.9.4 [hive.modifiers] p4 complexity is stated as "Constant. Exactly one object of type `T` is constructed."

However the approach to implementation necessary to support 8/16-bit types without artificially widening the type storage, as described under "Additional info for supporting small types" in P0447 basically specifies time complexity which is `Log(n)` (on most platforms, log64 or log32) in the capacity of the element block selected to insert into. This time complexity only occurs during the operation to find an erased element memory location within a block which is known to have one, and does not involve the elements themselves.

This is both the simplest and fastest solution to supporting small types in hive without artificial widening that I have come across.

Further, I have discovered that this approach can be extended to larger block capacities via "bitset stacking" while retaining `𝒪(log n)` intra-block lookup time complexity, regardless of block capacity. Overall this approach would be useful for embedded and other memory-scarse platforms as it reduces the 16-bit-per-element cost of the reference implementation down to a 1-bit-per-element cost. For 64-bit and larger types, there are other ways to obtain this reduction without losing `𝒪(1)` lookup but it is unclear whether those methods would in fact be faster. For approaches involving bitset-stacking, the logarithmic complexity also occurs during erasure, specifically adding a couple of extra instructions for every 64x (i.e. bit-depth) increase in block capacity. But again this does not involve the elements and is logarithmic in the capacity of the block erased from.

Regardless, the increased complexity during insertion is necessary for small-type support.

There was ambiguity as to whether this should result in a change to `hive` time complexity when discussed on the reflector, as it is unrelated to element numbers (unless all elements fit within one block), but it is related to block capacities, which are defined as part of the `hive` technical specification.
Previous resolution [SUPERSEDED]

This wording is relative to N5014.

> [Drafting note: I am unclear on whether `assign()` and `assign_range()` operations would require specification since they also have the capability to reuse existing erased element memory spaces, but we do not currently supply time complexity wording for these in the standard in general and I'm unsure why that is.]

1. Modify 23.3.9.1 [hive.overview] as indicated:

   > -1- A `hive` is a type of sequence container that provides constant-time insertion and erasure operations. Swhere storage is automatically managed in multiple memory blocks, referred to as element blocks. Insertion position is determined by the container, and insertion may re-use the memory locations of erased elements. Insertions are either constant time or logarithmic in the capacity of the element block inserted into.

   > -2- […]

   > -3- Erasures use unspecified techniques of constant time complexity to identify the memory locations of erased elements, which are subsequently skipped during iteration in constant time, as opposed to relocating subsequent elements during erasure. These techniques are either constant time or logarithmic in the capacity of the element block erased from.

2. Modify 23.3.9.2 [hive.cons] as indicated:

   >     hive& operator=(const hive& x);

   > > -25- Preconditions: […]

   > > -26- Effects: […]

   > > -27- Complexity: Linear in `size() + x.size()`. Additionally at worst `𝒪(log n)` in the capacity of each element block which an element is constructed within.

   >     hive& operator=(hive&& x)
   >       noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
   >                allocator_traits<Allocator>::is_always_equal::value);

   > > -28- Preconditions: […]

   > > -29- Effects: […]

   > > -30- Postconditions: […]

   > > -31- Complexity: Linear in `size()`. If

   > > >     (allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
   > > >     get_allocator() == x.get_allocator())

   > > is `false`, also linear in `x.size()` and additionally at worst `𝒪(log n)` in the capacity of each element block which an element is constructed within.

3. Modify 23.3.9.3 [hive.capacity] as indicated:

   >     void shrink_to_fit();

   > > -8- Preconditions: […]

   > > -9- Effects: […]

   > > -10- Complexity: If reallocation happens, linear in the size of the sequence and at worst `𝒪(log n)` in the capacity of each element block which elements are reallocated into.

   > > -11- Remarks: […]

   > […]

   >     void reshape(hive_limits block_limits);

   > > -21- Preconditions: […]

   > > -22- Effects: […]

   > > -23- Postconditions: […]

   > > -24- Complexity: Linear in the number of element blocks in `*this`. If reallocation happens, also linear in the number of elements reallocated and at worst `𝒪(log n)` in the capacity of each element block which elements are reallocated into.

   > > -25- Remarks: […]

4. Modify 23.3.9.4 [hive.modifiers] as indicated:

   >     template<class... Args> iterator emplace(Args&&... args);
   >     template<class... Args> iterator emplace_hint(const_iterator hint, Args&&... args);

   > > -1- Preconditions: […]

   > > -2- Effects: […]

   > > -3- Returns: […]

   > > -4- Complexity: ConstantAt worst `𝒪(log n)` in the capacity of the element block which the element is constructed within. Exactly one object of type `T` is constructed.

   > […]

   >     void insert(initializer_list<T> rg);
   >     template<container-compatible-range<T> R>
   >       void insert_range(R&& rg);

   > > -7- Preconditions: […]

   > > -8- Effects: […]

   > > -9- Complexity: Linear in the number of elements inserted. Additionally at worst `𝒪(log n)` in the capacity of each element block which an element is constructed within. Exactly one object of type `T` is constructed for each element inserted.

   > > -10- Remarks: […]

   >     void insert(size_type n, const T& x);

   > > -11- Preconditions: […]

   > > -12- Effects: […]

   > > -13- Complexity: Linear in `n`. Additionally at worst `𝒪(log n)` in the capacity of each element block which an element is constructed within.. Exactly one object of type `T` is constructed for each element inserted.

   > > -14- Remarks: […]

   > […]

   >     iterator erase(const_iterator position);
   >     iterator erase(const_iterator first, const_iterator last);

   > > -16- Complexity: Linear in the number of elements erased and for each erased element at worst `𝒪(log n)` in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.

5. Modify 23.3.9.5 [hive.operations] as indicated:

   > [Drafting note: If issue LWG 4323(i) is decided to be acted upon and the proposed solution accepted, the proposed complexity wording becomes:

   > > -11- Complexity: If `empty()` is `false`, exactly `size() - 1` applications of the corresponding predicate, otherwise no applications of the predicate. For each element erased as a result of this function call, at worst `𝒪(log n)` in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.

   > ]

   >     template<class BinaryPredicate = equal_to<T>>
   >       size_type unique(BinaryPredicate binary_pred = BinaryPredicate());

   > > -7- Preconditions: […]

   > > -8- Effects: […]

   > > -9- Returns: […]

   > > -10- Throws: […]

   > > -11- Complexity: If `empty()` is `false`, exactly `size() - 1` applications of the corresponding predicate, otherwise no applications of the predicate. Additionally, for each element erased as a result of this function call, at worst `𝒪(log n)` in the capacity of each block containing the element.

   > > -12- Remarks: […]

[2025-09-26; Matt comments and provides improved wording]

Past LWG/LEWG telecon discussion around this topic concluded that because elements are not involved, and the logarithmic action is within the capacity of a block (a fixed number), not the size of the sequence, and the actual performance cost is negligible, that the complexity of these actions are in fact constant. But there is some disagreement on this.

One possibility is to add additional complexity data to each of the effected functions. This would impact on `emplace`, range `insert`, fill `insert`, `shrink_to_fit`, `reshape`, copy/move assignment operator, `erase` and `unique`. However I feel this is overkill and may confuse implementors as the log(n) complexity is not permitted to involve elements.

Having looked into it and sought feedback I think a blanket note on 23.3.9.1 [hive.overview] p3 would be sufficient, such that the time complexity is limited to "techniques to identify the memory locations of erased elements". Otherwise we need to stay with the previous resolution that this is in fact constant time behaviour.

[2026-02-18; Reflector poll.]

Set priority to 3 after reflector poll.

We probably want to consider all container complexity specifications holistically.

The note in the proposed resolution is granting permission that is not stated by any normative wording; an alternative was suggested.

Proposed resolution:

This wording is relative to N5014.

1. Modify 23.3.9.1 [hive.overview] as indicated:

   > -1- A `hive` is a type of sequence container that provides constant-time insertion and erasure operations. Storage is automatically managed in multiple memory blocks, referred to as element blocks. Insertion position is determined by the container, and may re-use the memory locations of erased elements.

   > -2- […]

   > -3- Erasures use unspecified techniques of constant time complexity to identify the memory locations of erased elements, which are subsequently skipped during iteration in constant time, as opposed to relocating subsequent elements during erasure. The same or different techniques may be utilized to find and re-use these locations during subsequent insertions.

   > [Note: The techniques are permitted to be at worst logarithmic in the capacity of the element blocks being inserted into or erased from, while maintaining constant-time iteration, to allow latitude for implementation-specific optimizations. — end note]

### 4322(i). Problematic Constraints on incomplete types in indirect and polymorphic

Section: 20.4 [mem.composite.types] Status: New Submitter: Jonathan B. Coe Opened: 2025-08-20 Last modified: 2025-10-07

Priority: 2

View all issues with New status.

Discussion:

The class templates `indirect<T>` and `polymorphic<T>` allow the template argument `T` to be an incomplete type.

Both classes can be instantiated when the type `T` is incomplete: constraints are written so that requirements on incomplete types are not evaluated at class instantiation time.

For constructors with additional template parameters, there are currently constraints written on the potentially incomplete type `T` and the additional template parameters. Such constraints will not be evaluated at class instantiation time but could be explicitly evaluated in contexts where support for an incomplete `T` is required.

>     template<typename U>
>     class A {
>       U u;
>     public:
>       A(const SomeType&) requires std::is_constructible_v<U, SomeType>
>       {
>         // […]
>       }
>     };

when `U` is `indirect<T>` or `polymorphic<T>` for some type `T`, the existence of the requires clause will require that `T` is a complete type for constraints on indirect or polymorphic to be evaluated.

Constraints on `T` should be converted to Mandates on `T` so that constraint evaluation does not require `T` to be a complete type.

[2025-10-07; Reflector poll]

Set priority to 2 after reflector poll.

"Do we have a concrete use case of an A-like type? Is making the traits give wrong answers preferable to hard errors? I don't think the `polymorphic` part makes sense. Since the constraints still require `U`/`UU` to be complete, either `T` is a base class and it must be complete, or it is not and it doesn't matter."

"The problematic members are non-template functions, so they are instantiated early and the requires clauses are checked too soon. Think this can be solved without giving up SFINAE-friendliness, by making the affected members themselves templates."

Proposed resolution:

This wording is relative to N5014.

1. Modify 20.4.1.3 [indirect.ctor] as indicated:

   >     template<class U = T>
   >       constexpr explicit indirect(U&& u);

   > > -17- Constraints:

   > > 1. (17.1) — `is_same_v<remove_cvref_t<U>, indirect>` is `false`,

   > > 2. (17.2) — `is_same_v<remove_cvref_t<U>, in_place_t>` is `false`, and

   > > 3. (17.3) — `is_constructible_v<T, U>` is `true`, and

   > > 4. (17.4) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `is_constructible_v<T, U>` is `true`.

   > > -18- Effects: […]

   >     template<class U = T>
   >       constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u);

   > > -19- Constraints:

   > > 1. (19.1) — `is_same_v<remove_cvref_t<U>, indirect>` is `false`, and

   > > 2. (19.2) — `is_same_v<remove_cvref_t<U>, in_place_t>` is `false`., and

   > > 3. (19.3) — `is_constructible_v<T, U>` is `true`

   > > -?- Mandates: `is_constructible_v<T, U>` is `true`.

   > > -20- Effects: […]

   >     template<class... Us>
   >       constexpr explicit indirect(in_place_t, Us&&... us);

   > > -21- Constraints:

   > > 1. (21.1) — `is_constructible_v<T, Us...>` is `true`, and

   > > 2. (21.2) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `is_constructible_v<T, Us...>` is `true`.

   > > -22- Effects: […]

   >     template<class... Us>
   >       constexpr explicit indirect(allocator_arg_t, const Allocator& a,
   >                                   in_place_t, Us&& ...us);

   > > -23- ConstraintsMandates: `is_constructible_v<T, Us...>` is `true`

   > > -24- Effects: […]

   >     template<class I, class... Us>
   >       constexpr explicit indirect(in_place_t, initializer_list<I> ilist, Us&&... us);

   > > -25- Constraints:

   > > 1. (25.1) — `is_constructible_v<T, initializer_list<I>&, Us...>` is `true`, and

   > > 2. (25.2) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `is_constructible_v<T, initializer_list<I>&, Us...>` is `true`.

   > > -26- Effects: […]

   >     template<class I, class... Us>
   >       constexpr explicit indirect(allocator_arg_t, const Allocator& a,
   >                                   in_place_t, initializer_list<I> ilist, Us&&... us);

   > > -27- ConstraintsMandates: `is_constructible_v<T, initializer_list<I>&, Us...>` is `true`

   > > -28- Effects: […]

2. Modify 20.4.2.3 [polymorphic.ctor] as indicated:

   >     template<class U = T>
   >       constexpr explicit polymorphic(U&& u);

   > > -12- Constraints: Where `UU` is `remove_cvref_t<U>`,

   > > 1. (12.1) — `is_same_v<UU, polymorphic>` is `false`,

   > > 2. (12.2) — `derived_from<UU, T>` is `true`,

   > > 3. (12.3) — `is_constructible_v<UU, U>` is `true`,

   > > 4. (12.4) — `is_copy_constructible_v<UU>` is `true`,

   > > 5. (12.5) — `UU` is not a specialization of `in_place_type_t`, and

   > > 6. (12.6) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `derived_from<UU, T>` is `true`.

   > > -13- Effects: […]

   >     template<class U = T>
   >       constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, U&& u);

   > > -14- Constraints: Where `UU` is `remove_cvref_t<U>`,

   > > 1. (14.1) — `is_same_v<UU, polymorphic>` is `false`,

   > > 2. (14.2) — `derived_from<UU, T>` is `true`,

   > > 3. (14.3) — `is_constructible_v<UU, U>` is `true`,

   > > 4. (14.4) — `is_copy_constructible_v<UU>` is `true`, and

   > > 5. (14.5) — `UU` is not a specialization of `in_place_type_t`.

   > > -?- Mandates: `derived_from<UU, T>` is `true`.

   > > -15- Effects: […]

   >     template<class U, class... Ts>
   >       constexpr explicit polymorphic(in_place_type_t<U>, Ts&&... ts);

   > > -16- Constraints:

   > > 1. (16.1) — `is_same_v<remove_cvref_t<U>, U>` is `true`,

   > > 2. (16.2) — `derived_from<U, T>` is `true`,

   > > 3. (16.3) — `is_constructible_v<U, Ts>` is `true`,

   > > 4. (16.4) — `is_copy_constructible_v<U>` is `true`, and

   > > 5. (16.5) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `derived_from<U, T>` is `true`.

   > > -17- Effects: […]

   >     template<class U, class... Ts>
   >       constexpr explicit polymorphic(allocator_arg_t, const Allocator& a,
   >                                      in_place_type_t<U>, Ts&&... ts);

   > > -18- Constraints:

   > > 1. (18.1) — `is_same_v<remove_cvref_t<U>, U>` is `true`,

   > > 2. (18.2) — `derived_from<U, T>` is `true`,

   > > 3. (18.3) — `is_constructible_v<U, Ts>` is `true`, and

   > > 4. (18.4) — `is_copy_constructible_v<U>` is `true`.

   > > -?- Mandates: `derived_from<U, T>` is `true`.

   > > -19- Effects: […]

   >     template<class U, class I, class... Us>
   >       constexpr explicit polymorphic(in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);

   > > -20- Constraints:

   > > 1. (20.1) — `is_same_v<remove_cvref_t<U>, U>` is `true`,

   > > 2. (20.2) — `derived_from<U, T>` is `true`,

   > > 3. (20.3) — `is_constructible_v<U, initializer_list<I>&, Us...>` is `true`,

   > > 4. (20.4) — `is_copy_constructible_v<U>` is `true`, and

   > > 5. (20.5) — `is_default_constructible_v<Allocator>` is `true`.

   > > -?- Mandates: `derived_from<U, T>` is `true`.

   > > -21- Effects: […]

   >     template<class U, class I, class... Us>
   >       constexpr explicit polymorphic(allocator_arg_t, const Allocator& a,
   >                                      in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);

   > > -22- Constraints:

   > > 1. (22.1) — `is_same_v<remove_cvref_t<U>, U>` is `true`,

   > > 2. (22.2) — `derived_from<U, T>` is `true`,

   > > 3. (22.3) — `is_constructible_v<U, initializer_list<I>&, Us...>` is `true`, and

   > > 4. (22.4) — `is_copy_constructible_v<U>` is `true`.

   > > -?- Mandates: `derived_from<U, T>` is `true`.

   > > -23- Effects: […]

### 4323(i). `hive::unique` time complexity should incorporate potential block removal complexity

Section: 23.3.9.5 [hive.operations] Status: New Submitter: Matt Bentley Opened: 2025-08-24 Last modified: 2025-12-04

Priority: 3

View other active issues in [hive.operations].

View all other issues in [hive.operations].

View all issues with New status.

Discussion:

Currently we specify for `erase` 23.3.9.4 [hive.modifiers] p16:

> Complexity: Linear in the number of elements erased. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.

This is to allow for potential `hive` implementations as vectors of pointers to blocks, as opposed to linked-lists of blocks (reference implementation approach). In that scenario removing a block would involve a vector erasure and subsequent pointer-to-block relocations. swap-and-pop of block pointers would not work as this would re-arrange the sequence during erasure. Anyway, we have neglected to apply this same complexity to `hive::unique`. It would be rare that this would occur for `unique`, as it would involve (1) a block A with only one non-erased element left in it and (2) a block B preceding/following block A in the sequence, whose last/first element is equal to the element in block A. But it is possible, so the same consideration in terms of time complexity applies.

> This is a separate but related issue to LWG 4320(i), but it would affect the outcome wording of that issue for `unique`.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

Related to LWG 4380(i).

Proposed resolution:

This wording is relative to N5014.

1. Modify 23.3.9.5 [hive.operations] as indicated:

   > [Drafting note: If issue LWG 4320(i) is decided to be acted upon and the proposed solution accepted, the proposed complexity wording becomes:

   > > -11- Complexity: If `empty()` is `false`, exactly `size() - 1` applications of the corresponding predicate, otherwise no applications of the predicate. For each element erased as a result of this function call, at worst `𝒪(log n)` in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.

   > ]

   >     template<class BinaryPredicate = equal_to<T>>
   >       size_type unique(BinaryPredicate binary_pred = BinaryPredicate());

   > > -7- Preconditions: […]

   > > -8- Effects: […]

   > > -9- Returns: […]

   > > -10- Throws: […]

   > > -11- Complexity: If `empty()` is `false`, exactly `size() - 1` applications of the corresponding predicate, otherwise no applications of the predicate. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.

   > > -12- Remarks: […]

### 4326(i). Standard defined queries can't customize `forwarding_query_t` by inheriting from it

Section: 33.5.2 [exec.fwd.env] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-17

Priority: 4

View all issues with New status.

Discussion:

The definition of the `forwarding_query_t` (33.5.2 [exec.fwd.env]) states that a query is a forwarding-query if either the expression `q.query(forwarding_query)` is a constant expression that evaluates to `true` or the query type inherits from `forwarding_query_t`.

However, all of the queries defined in 33 [exec] are defined similar to the following:

>     struct get_allocator_t { unspecified };

While this definition allows for the members of the class to be unspecified, it still explicitly specifies that it has an empty list of base-classes and therefore cannot customize `forwarding_query_t` by inheriting from `forwarding_query_t`.

We should consider whether we want to change the specification of queries that are to be forwarding-queries to allow inheritance from `forwarding_query_t` or otherwise remove the support for customizing `forwarding_query_t` by inheriting from it.

Proposed Approach:

Remove the rule that checks for inheritance from `forwarding_query_t`. Any query can already customize `forwarding_query_t` by defining a `query(forwarding_query_t)` member function, so there is no loss of functionality from doing this and it would simplify the design.

[2025-10-17; Reflector poll.]

Set priority to 4 after reflector poll.

"The fact that we didn't use customization by derivation yet doesn't mean we should remove that customization mechanism."

Proposed resolution:

This wording is relative to N5014.

1. Modify 33.5.2 [exec.fwd.env] as indicated:

   > -2- The name `forwarding_query` denotes a query object. For some query object `q` of type `Q`, `forwarding_query(q)` is expression-equivalent to:

   > 1. (2.1) — `MANDATE-NOTHROW(q.query(forwarding_query))` if that expression is well-formed.

   >    Mandates: The expression above has type `bool` and is a core constant expression if `q` is a core constant expression.

   > 2. (2.2) — Otherwise, `true` if `derived_from<Q, forwarding_query_t>` is `true`.

   > 3. (2.3) — Otherwise, `false`.

### 4327(i). Equal schedulers should be required to have same behaviour

Section: 33.6 [exec.sched] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-23

Priority: 3

View all other issues in [exec.sched].

View all issues with New status.

Discussion:

The wording 33.6 [exec.sched] p4 says:

> For any two values `sch1` and `sch2` of some scheduler type `Sch`, `sch1 == sch2` shall return `true` only if both `sch1` and `sch2` share the same associated execution resource.

However, I don't think this requirement is sufficient for schedulers to be considered equal as it is possible to have two different schedulers that share the same associated execution resource but that have different behaviour.

For example, two schedulers of the same type and having the same associated execution resource but where one schedules items with high priority and others with low priority.

Ideally we want two schedulers to be equal if and only if they can be used interchangeably such that using either scheduler to schedule work has the same behaviour.

Note that some use cases may also want to compare two schedulers as to whether or not they share the same associated execution resource, independently of their scheduling behaviour, however this could be added as a separate operator later, e.g. `std::execution::same_resource(sch1, sch2)`.

While the above wording does still permit implementations to return `false` even if the associated execution resources are the same, it is not clear how this definition applies to schedulers such as an `inline_scheduler` (added in P3552) in which has no specific associated execution resource but where instances nevertheless have the same scheduling behaviour.

One approach to improving this would be to change p4 to refer to the schedulers having the same scheduling behaviour rather than having the same associated execution resource.

For example, modify 33.6 [exec.sched] p4 to refer to schedulers having the same scheduling behaviour rather than having the same associated execution resource.

> For any two values `sch1` and `sch2` of some scheduler type `Sch`, `sch1 == sch2` shall return `true` only if both `sch1` and `sch2` share the same associated execution resourcehave the same scheduling behaviour.

However, this then raises the question of exactly what "the same scheduling behaviour" is. For example, what if two schedulers have the same associated execution resource and the same behaviour for a `schedule()` operation, but have different behaviours for customizations of the `bulk()` algorithm?

Alternatively, we could talk about two schedulers being equal if and only if they can be used interchangeably throughout the program.

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

[2025-10-23; .]

"Would prefer if 'the same scheduling behavior' was added, instead of replacing existing wording."

"`scheduler` requires `equality_comparable`, so equality of schedulers must be equality preserving, so equality already implies scheduler interchangeability. Any additional specification is redundant and should be struck."

"I think `task_scheduler(parallel_scheduler) == parallel_scheduler` currently (and possibly incorrectly) says true, because they share the same execution resource. But the behaviour is dramatically different: when using `bulk` the first will execute sequentially and the second will execute in parallel."

Proposed resolution:

### 4338(i). sender unaware coroutines should be able to `co_await` a `task`

Section: 33.13.6.2 [task.class] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23

Priority: 2

View other active issues in [task.class].

View all other issues in [task.class].

View all issues with LEWG status.

Discussion:

The `task` type doesn't have an `operator co_await()`. Thus, a `task` can't be `co_await`ed directly from a coroutine. Using `as_awaitable` on the `task` object also doesn't work because this function requires a reference to the promise as second argument.

`task` could define an `operator co_await()` that returns an awaitable object or directly provide an awaiter interface. There are, however, two complications:

1. For scheduler affinity the `task` needs to get a scheduler from the object starting the `task`. While the promise can be obtained from the coroutine handle passed to `await_suspend()` these normally wouldn't have an associated environment supporting a `get_scheduler` query.
2. The approach to reporting cancellation is to call a function `unhandled_stopped()` on the promise type which is generally not available. It could be argued that cancellation isn't really a problem because the it is unlikely that the environment associated with the promise supports a `get_stop_token` query.

It is worth noting that senders in general do not support an `operator co_await()`, i.e., other senders are also not awaitable directly. On the other hand, `task` could become the generic adapater to make senders awaitable from coroutines.

[2025-10-23; Reflector poll. Status changed: New → LEWG]

Set priority to 2 after reflector poll. Send to LEWG.

Proposed resolution:

### 4348(i). `task` doesn't support symmetric transfer

Section: 33.13.6.2 [task.class] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17

Priority: 2

View other active issues in [task.class].

View all other issues in [task.class].

View all issues with LEWG status.

Discussion:

The specification of `task` doesn't require symmetric transfer which can help with stack overflow. Also, when another `task` is `co_await`ed the scheduler on which the `task` resumes is known and can be used to avoid unnecessary scheduling by comparing the scheduler currently installed by in two `task`s involved.

[2025-10-17; Reflector poll. Status changed: New → LEWG.]

Set priority to 2 after reflector poll. Send to LEWG.

Proposed resolution:

### 4350(i). Should flat adaptors use `insert_range` when available?

Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: LEWG Submitter: Jonathan Wakely Opened: 2025-09-05 Last modified: 2025-10-21

Priority: 2

View other active issues in [flat.map].

View all other issues in [flat.map].

View all issues with LEWG status.

Discussion:

As specified in 23.6.3.4 [queue.mod], `std::queue::push_range` is guaranteed to use `c.append_range` if that exists. For `flat_map` and its flat friends, we only ever use `c.insert` and never try to use `c.insert_range`, `c.append_range`, etc. LWG thinks the "as if by" wording allows implementations to use `insert_range` when the container type being adapted is `std::vector` but not for user-defined containers. Should the flat adaptors follow `std::queue` and guarantee that they will use the `xxx_range` member if they exist? That would mean that user containers need to ensure that any member functions with those names must behave as expected, but we already require that for `std::queue`.

[2025-10-21; Reflector poll. Status changed: New → LEWG.]

Set priority to 2 after reflector poll.

Use `append_range` to be consistent with other adapters. Send to LEWG for confirmation.

Proposed resolution:

### 4352(i). "operations on flat_multimap are equivalent to those of flat_map"

Section: 23.6.9.1 [flat.multimap.overview] Status: New Submitter: Jonathan Wakely Opened: 2025-09-05 Last modified: 2025-10-21

Priority: 3

View all issues with New status.

Discussion:

23.6.9.1 [flat.multimap.overview] p4 says:

> Except as otherwise noted, operations on `flat_multimap` are equivalent to those of `flat_map`, except that `flat_multimap` operations do not remove or replace elements with equal keys.

> [Example 1:`flat_multimap` constructors and emplace do not erase non-unique elements after sorting them. — end example]

This doesn't really work, because there is no `flat_map` operation that is equivalent to the constructors and `insert` and `insert_range` functions that take a `sorted_equivalent_t` tag argument. The `flat_map` operations that the wording intends to refer to take a `sorted_unique_t` tag argument. It's a bit of a stretch to say they're equivalent when they don't even have the same parameters, so we need some weasel words here.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Define them in terms of `insert_range(std::forward<R>(rg))` with updated Complexity.

Proposed resolution:

### 4353(i). Uses of `MANDATE-NOTHROW` in CPOs should not enclose CPO argument sub-expressions

Section: 33 [exec] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-23

Priority: 3

View all issues with New status.

Discussion:

There are a number of CPOs defined in 33 [exec] which have behaviour specified in terms of being expression-equivalent to a `MANDATE-NOTHROW` expression.

The intent of this is that we want to make sure that the call that the CPO dispatches to is marked `noexcept`.

However, the way that these CPOs are currently specified in terms of sub-expressions means that we are currently requiring that all of the expressions passed as arguments to the CPO are also `noexcept`. Outside of defining these CPOs as preprocessor macros, this is unimplementable — and also undesirable behaviour.

For example, 33.7.2 [exec.set.value] defines `set_value(rcvr, vs...)` to be equivalent to `MANDATE-NOTHROW(rcvr.set_value(vs...))` for sub-expressions `rcvr` and pack of sub-expressions `vs`.

In 33.1 [exec.general] p5 we define `MANDATE-NOTHROW(expr)` as expression-equivalent to `expr` but mandate that `noexcept(expr)` is `true`.

So in the above definition of `set_value(rcvr, vs...)` we are actually requiring that the expression `noexcept(rcvr.set_value(vs...))` is `true`.

This is only true if all of the sub-expressions are `noexcept`, i.e. all of the following expressions are `true`.

- `noexcept(rcvr)`,

- `(noexcept(vs) && ...)`,

- the member-function call to `rcvr.set_value(vs...)` including any implicit conversions of arguments.

This means that if, for example, one of the sub-expressions in the pack `vs` was a call to some potentially-throwing function then the overall `set_value` expression would be violating the mandates requirement.

For example:

>     struct my_receiver
>     {
>       void set_value(int x) noexcept;
>     };

>     int get_value() noexcept(false);

>     my_receiver r;
>     std::execution::set_value(r, get_value()); // fails MANDATE-NOTHROW mandates

Instead, we need to redefine these CPOs as being expression-equivalent to something that does not require that the argument expressions to the CPO themselves are `noexcept` — only what will be in the body of the CPO function.

For example, we could change 33.7.2 [exec.set.value] to define `set_value(rcvr, vs...)` as expression-equivalent to:

>     [](auto&& rcvr2, auto&&... vs2) noexcept ->
>       decltype(auto) requires requires { std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(vs2)...); }
>     {
>       return MANDATE-NOTHROW(std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(vs2)...));
>     }(rcvr, vs...)

The following sections all contain problematic uses of `MANDATE-NOTHROW`:

- 33.5.3 [exec.get.allocator]

- 33.5.4 [exec.get.stop.token]

- 33.5.5 [exec.get.env]

- 33.5.6 [exec.get.domain]

- 33.5.7 [exec.get.scheduler]

- 33.5.9 [exec.get.delegation.scheduler]

- 33.5.10 [exec.get.fwd.progress]

- 33.5.11 [exec.get.compl.sched]

- 33.5.13 [exec.get.await.adapt]

- 33.7.2 [exec.set.value]

- 33.7.3 [exec.set.error]

- 33.7.4 [exec.set.stopped]

- 33.8.2 [exec.opstate.start]

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4355(i). `connect-awaitable()` should mandate `rcvr` can receive all completion-signals rather than using constraints

Section: 33.9.10 [exec.connect] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23

Priority: 2

View other active issues in [exec.connect].

View all other issues in [exec.connect].

View all issues with New status.

Discussion:

In 33.9.10 [exec.connect] p6, the wording for the `connect()` customization-point defines a case that is expression-equivalent to `connect-awaitable(new_sndr, rcvr)`.

The `connect-awaitable` function is specified in p5 as having the signature:

>     operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs>;

The requires-clause constrains this overload to ensure that the receiver, `rcvr`, can accept all of the completion signatures listed in `Sigs`. This means that `connect()` will SFINAE out if the receiver cannot accept all of the completion signatures, rather than this being ill-formed.

However, this seems inconsistent with the handling of the case that dispatches to `new-sndr.connect(rcvr)` which does not constrain on this. It is also redundant with the Mandates element in p6 which mandates that the following is satisfied:

>     receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>

The constraint on `connect-awaitable` should either be removed or replaced with a Mandates element. As `connect-awaitable` is only used within the definition of `connect()` and as `connect()` already mandates that `receiver_of` is satisfied, it seems sufficient to just remove this constraint from `connect-awaitable`.

[2025-10-23; Reflector poll.]

Set priority to 2 after reflector poll.

"Is this actually redundant? Where is `Sigs` required to match (or at least be a subset of) `completion_signatures_of<Sndr, env_of_t<Recvr>>`?"

Proposed resolution:

This wording is relative to N5014.

1. Modify 33.9.10 [exec.connect] as indicated:

   > -5- Let `V` name the type `await-result-type<DS, connect-awaitable-promise>`, let `Sigs` name the type

   > >     completion_signatures<
   > >       SET-VALUE-SIG(V), // see 33.9.3 [exec.snd.concepts]
   > >       set_error_t(exception_ptr),
   > >       set_stopped_t()>

   > and let `connect-awaitable` be an exposition-only coroutine defined as follows:

   > >     namespace std::execution {
   > >       […]
   > >       operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
   > >         exception_ptr ep;
   > >         try {
   > >           if constexpr (same_as<V, void>) {
   > >             co_await std::move(sndr);
   > >             co_await suspend-complete(set_value, std::move(rcvr));
   > >           } else {
   > >             co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
   > >           }
   > >         } catch(...) {
   > >           ep = current_exception();
   > >         }
   > >         co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
   > >       }
   > >     }

### 4356(i). `connect()` should use `get_allocator(get_env(rcvr))` to allocate the coroutine-state for a `connect-awaitable` coroutine

Section: 33.9.10 [exec.connect] Status: Open Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2026-05-19

Priority: 2

View other active issues in [exec.connect].

View all other issues in [exec.connect].

View all issues with Open status.

Discussion:

The wording for `connect()` (33.9.10 [exec.connect]) handles passing awaitable types as the sender argument by calling the `connect-awaitable`() coroutine and having it execute a `co_await` expression.

The `connect-awaitable`() coroutine will typically need to dynamically allocate storage for the coroutine state and, as specified, this currently just always uses the global default allocator. This is because the `connect-awaitable-promise` type does not define any member operator `new/delete` overloads.

It seems desirable for this facility to use the allocator obtained from the receiver, by calling `get_allocator(get_env(rcvr))`, in order to allocate storage for the coroutine-state instead of always using global operator `new`. This would give the user at least some level of control over how this allocation is performed.

[2025-10-23; Reflector poll.]

Set priority to 2 after reflector poll.

"The `get_allocator` query doesn't have a default and I think that isn't covered in the proposed resolution."

[2026-05-15; LWG telecon. Status: New → Open.]

SG1 discussed this in November and responded:

> Poll: SG1 recommends to specify that the destruction and the deallocation of coroutine state happens before emitting senders/receivers completion signal for LWG4356. Outcome: Strong consensus in favor.

LWG discussion decided that the wording needs to say something like "using the allocator obtained from `get_allocator(get_env(rcvr))` if that expression is well-formed, and from `allocator<byte>()` otherwise." The ordering problem identified by SG1 wouldn't be a problem if it always used `std::allocator`, but obtaining an allocator from the environment introduces the potential lifetime issue. Since everything in 33.9.10 [exec.connect] is specified in code, that code should be updated to include obtaining and using an allocator for the coroutine frame, and destroying and deallocating everything before the completion signal. This needs a lot of work.

Proposed resolution:

This wording is relative to N5014.

1. Modify 33.9.10 [exec.connect] as indicated:

   > [Drafting note: This should cover the design intent, although we may want to spell this out more explicitly in terms of the exact semantics in a similar way to 25.8.5 [coro.generator.promise] p17, which lists overloads of operator `new()` and describes the rebound allocator type which allocates storage in chunks of size `__STDCPP_DEFAULT_NEW_ALIGNMENT__`.]

   > -5- Let `V` name the type `await-result-type<DS, connect-awaitable-promise>`, let `Sigs` name the type

   > >     […]

   > and let `connect-awaitable` be an exposition-only coroutine defined as follows:

   > >     namespace std::execution {
   > >       […]
   > >     }

   > Any dynamically allocated storage required for the coroutine state allocated by an invocation of the form `connect-awaitable(sndr, rcvr)` is allocated using the allocator obtained from `get_allocator(get_env(rcvr))`.

   > -6- […]

### 4357(i). `connect-awaitable` should use `is_void_v` to check for result-type of `co_await` expression instead of `same_as<void>`

Section: 33.9.10 [exec.connect] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23

Priority: 4

View other active issues in [exec.connect].

View all other issues in [exec.connect].

View all issues with New status.

Discussion:

The wording in 33.9.10 [exec.connect] p5 defines the `connect-awaitable()` function as follows:

>     operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
>       exception_ptr ep;
>       try {
>         if constexpr (same_as<V, void>) {
>           co_await std::move(sndr);
>           co_await suspend-complete(set_value, std::move(rcvr));
>         } else {
>           co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
>         }
>       } catch(...) {
>         ep = current_exception();
>       }
>       co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
>     }

The use of `same_as<V, void>` in the if-constexpr condition does not cover the case where the result of the `co_await` expression has type cv `void`. It should use `is_void_v<V>` instead to allow it to match all `void` types, not just unqualified `void`.

[2025-10-23; Reflector poll.]

Set priority to 4 after reflector poll.

"NAD, `V` is `await-result-type<...>` which is `decltype(...)` and you never get cv void from `decltype` (7.2.2 [expr.type] p2)."

"But using `is_void_v` more clearly expresses the intent."

Proposed resolution:

This wording is relative to N5014.

1. Modify 33.9.10 [exec.connect] as indicated:

   > -5- Let `V` name the type `await-result-type<DS, connect-awaitable-promise>`, let `Sigs` name the type

   > >     completion_signatures<
   > >       SET-VALUE-SIG(V), // see 33.9.3 [exec.snd.concepts]
   > >       set_error_t(exception_ptr),
   > >       set_stopped_t()>

   > and let `connect-awaitable` be an exposition-only coroutine defined as follows:

   > >     namespace std::execution {
   > >       […]
   > >       operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
   > >         exception_ptr ep;
   > >         try {
   > >           if constexpr (same_as<V, void>is_void_v<V>) {
   > >             co_await std::move(sndr);
   > >             co_await suspend-complete(set_value, std::move(rcvr));
   > >           } else {
   > >             co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
   > >           }
   > >         } catch(...) {
   > >           ep = current_exception();
   > >         }
   > >         co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
   > >       }
   > >     }

### 4364(i). SFINAE-friendliness on `operator<<` and `operator>>` for unspecified I/O manipulators

Section: 31.7.7 [std.manip], 31.7.8 [ext.manip], 31.7.9 [quoted.manip] Status: New Submitter: Jiang An Opened: 2025-09-05 Last modified: 2025-10-21

Priority: 4

View other active issues in [std.manip].

View all other issues in [std.manip].

View all issues with New status.

Discussion:

Currently, it is unspecified whether any of `operator<<` or `operator>>` for the "unspecified" return type of an I/O manipulating function is SFINAE-friendly.

There's implementation divergence. E.g. the following program is consistently rejected by implementations although the standard doesn't strictly require it to be ill-formed. Among the implementations, libstdc++ and libc++ make the failure SFINAE-friendly, while MSVC STL doesn't (demo):

>     #include <iostream>
>     #include <iomanip>

>     int main()
>     {
>       std::cout << std::setfill(L'*');
>     }

It seems better to eliminate such implementation divergence. Perhaps it's also better to require them to be SFINAE-friendly to reduce potential conflicts with user-provided operators.

If it's intended to allow implementation divergence, perhaps we should clarify the intent.

[2025-10-21; Reflector poll.]

Set priority to 4 after reflector poll.

"Does it matter in practice? If the type is unspecified, it's fine for misuse to be non-portable."

"Would like a diagnostic, don't care about SFINAE. Not clear to me that the current `setfill` wording disallows the mixed-character-type case at all ..."

"NAD, not been a problem for a quarter of a century."

Proposed resolution:

### 4365(i). `boyer_moore_searcher` and `boyer_moore_horspool_searcher` should be `constexpr`-friendly

Section: 22.10.18.3 [func.search.bm], 22.10.18.4 [func.search.bmh] Status: LEWG Submitter: Hewill Kang Opened: 2025-09-03 Last modified: 2025-10-21

Priority: 4

View other active issues in [func.search.bm].

View all other issues in [func.search.bm].

View all issues with LEWG status.

Discussion:

Currently, `boyer_moore_searcher` and `boyer_moore_horspool_searcher` are not `constexpr`-friendly because their underlying implementation needs to precompute the shift table, which usually requires a `vector` or `unordered_map` to store.

Thanks to P3372R3, unordered containers are now `constexpr`-friendly. Although `std::hash` still lacks `constexpr` support, users can provide their own hash functions to use unordered containers at compile time.

Given that both `boyer_moore_searcher` and `boyer_moore_horspool_searcher` can take a custom hash, it makes perfect sense that they could be `constexpr`-friendly.

Not to mention that library implementations usually simply use arrays instead of hash tables for the common string case because characters only have 256 values, so `unordered_map` is not actually used.

[2025-10-21 Reflector poll; Status changed: New → LEWG and priority set to P4.]

Proposed resolution:

This wording is relative to N5014.

1. Modify 22.10.18.3 [func.search.bm] as indicated:

   > >     namespace std {
   > >       template<class RandomAccessIterator1,
   > >                class Hash = hash<typename iterator_traits<RandomAccessIterator1>::value_type>,
   > >                class BinaryPredicate = equal_to<>>
   > >       class boyer_moore_searcher {
   > >       public:
   > >         constexpr boyer_moore_searcher(RandomAccessIterator1 pat_first,
   > >                                        RandomAccessIterator1 pat_last,
   > >                                        Hash hf = Hash(),
   > >                                        BinaryPredicate pred = BinaryPredicate());

   > >         template<class RandomAccessIterator2>
   > >           constexpr pair<RandomAccessIterator2, RandomAccessIterator2>
   > >             operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > >       private:
   > >         RandomAccessIterator1 pat_first_;   // exposition only
   > >         RandomAccessIterator1 pat_last_;    // exposition only
   > >         Hash hash_;                         // exposition only
   > >         BinaryPredicate pred_;              // exposition only
   > >       };
   > >     }

   >     constexpr boyer_moore_searcher(RandomAccessIterator1 pat_first,
   >                                    RandomAccessIterator1 pat_last,
   >                                    Hash hf = Hash(),
   >                                    BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.

   > > […]

   >     template<class RandomAccessIterator2>
   >       constexpr pair<RandomAccessIterator2, RandomAccessIterator2>
   >         operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > > -5- Mandates: `RandomAccessIterator1` and `RandomAccessIterator2` have the same value type.

2. Modify 22.10.18.4 [func.search.bmh] as indicated:

   > >     namespace std {
   > >       template<class RandomAccessIterator1,
   > >                class Hash = hash<typename iterator_traits<RandomAccessIterator1>::value_type>,
   > >                class BinaryPredicate = equal_to<>>
   > >       class boyer_moore_horspool_searcher {
   > >       public:
   > >        constexpr boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first,
   > >                                                RandomAccessIterator1 pat_last,
   > >                                                Hash hf = Hash(),
   > >                                                BinaryPredicate pred = BinaryPredicate());

   > >         template<class RandomAccessIterator2>
   > >           constexpr pair<RandomAccessIterator2, RandomAccessIterator2>
   > >             operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > >       private:
   > >         RandomAccessIterator1 pat_first_;   // exposition only
   > >         RandomAccessIterator1 pat_last_;    // exposition only
   > >         Hash hash_;                         // exposition only
   > >         BinaryPredicate pred_;              // exposition only
   > >       };
   > >     }

   >     constexpr boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first,
   >                                             RandomAccessIterator1 pat_last,
   >                                             Hash hf = Hash(),
   >                                             BinaryPredicate pred = BinaryPredicate());

   > > -1- Preconditions: The value type of `RandomAccessIterator1` meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.

   > > […]

   >     template<class RandomAccessIterator2>
   >       constexpr pair<RandomAccessIterator2, RandomAccessIterator2>
   >         operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;

   > > -5- Mandates: `RandomAccessIterator1` and `RandomAccessIterator2` have the same value type.

### 4367(i). Improve `optional<T&>::or_else`

Section: 22.5.4.7 [optional.ref.monadic] Status: Ready Submitter: Hewill Kang Opened: 2025-09-07 Last modified: 2026-06-09

Priority: 4

View all other issues in [optional.ref.monadic].

View all issues with Ready status.

Discussion:

`optional<T&>::or_else` currently returns `*val` when it has a value, which calls the `optional(U&&)` constructor which in turn calls `convert-ref-init-val` which in turn calls `addressof`.

There's no reason to do that. It's much more efficient to call `optional<T&>`'s default copy constructor to just copy a pointer.

[2025-10-16; Reflector poll]

Set priority to 4 after reflector poll.

"NAD, no observable difference." "Still simpler to not have to reason about whether it matters, let's do it."

[Brno 2026-06-09; Status changed New → Ready.]

Proposed resolution:

This wording is relative to N5014.

1. Modify 22.5.4.7 [optional.ref.monadic] as indicated:

   >     template<class F> constexpr optional or_else(F&& f) const;

   > > -7- Constraints: `F` models `invocable`.

   > > -8- Mandates: `is_same_v<remove_cvref_t<invoke_result_t<F>>, optional>` is `true`.

   > > -9- Effects: Equivalent to:

   > > >     if (has_value()) {
   > > >       return *thisval;
   > > >     } else {
   > > >       return std::forward<F>(f)();
   > > >     }

### 4373(i). `function_ref` should provide `result_type`

Section: 22.10.17.6.2 [func.wrap.ref.class] Status: New Submitter: Hewill Kang Opened: 2025-09-12 Last modified: 2025-10-22

Priority: 3

View all issues with New status.

Discussion:

Currently, `function`, `move_only_function`, and `copyable_function` all have a type member `result_type`, but `function_ref` does not:

>     static_assert(is_same_v<          function    <int(int)>::result_type, int>);
>     static_assert(is_same_v<move_only_function    <int(int)>::result_type, int>);
>     static_assert(is_same_v< copyable_function    <int(int)>::result_type, int>);
>     static_assert(is_same_v<          function_ref<int(int)>::result_type, int>); // error

It seems worthwhile to also provide it for the latter, as it is consistent with the other wrappers and allows the user to easily extract the return type.

[2025-10-03; Hewill comments]

Additionally, given that some third-party implementations also provide a mechanism for extracting the return type, for example, `type_safe::function_ref` provides a public member type alias `return_type`, and `llvm::function_ref` can work with `llvm::function_traits` to obtain the return type, providing such a member type alias for `std::function_ref` meets user potential demands.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

Alternative (with incoming paper targeting LEWG) of deprecating `result_type` in existing wrappers was suggested.

Proposed resolution:

This wording is relative to N5014.

1. Modify 22.10.17.6.2 [func.wrap.ref.class] as indicated:

   >     namespace std {
   >       template<class R, class... ArgTypes>
   >       class function_ref<R(ArgTypes...) cv noexcept(noex)> {
   >       public:
   >         using result_type = R;

   >         // 22.10.17.6.3 [func.wrap.ref.ctor], constructors and assignment operators
   >         template<class F> function_ref(F*) noexcept;
   >         […]
   >       };

   >     }

### 4374(i). `flat_meow` range insertion behavior is unclear if in-place merge cannot allocate additional memory

Section: 23.6.8.7 [flat.map.modifiers], 23.6.11.5 [flat.set.modifiers], 23.6.12.5 [flat.multiset.modifiers] Status: New Submitter: Tim Song Opened: 2025-09-09 Last modified: 2025-10-21

Priority: 3

View all other issues in [flat.map.modifiers].

View all issues with New status.

Discussion:

The range insertion operations of `flat_map`/`multimap`/`set`/`multiset` have this Remarks: element:

> Since this operation performs an in-place merge, it may allocate memory.

It is not clear what happens if that allocation fails. If the `inplace_merge` algorithm is used, it will fall back to a less efficient `𝒪(N log N)` algorithm, but this is not reflected in the Complexity: element. Alternatively, if the allocation failure is reported by an exception, that should be called out in the spec.

[2025-10-21; Reflector poll.]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4380(i). `hive::reserve()` complexity does not reflect potential deallocation of blocks

Section: 23.3.9.3 [hive.capacity] Status: New Submitter: Matt Bentley Opened: 2025-09-23 Last modified: 2025-12-04

Priority: 3

View all other issues in [hive.capacity].

View all issues with New status.

Discussion:

As noted by Arthur in issue 4379(i), `reserve(n)` could choose to deallocate some reserved (empty) blocks if in doing so it could then allocate blocks which brought `capacity()` closer to `n`.

For example if `capacity()` is `460`, `n` is `480`, the `current-limits.min` is `40` and there is a reserved block of capacity `70`, the latter could be deallocated and a new reserved block allocated of capacity `90`, bringing total capacity to `480`. As opposed to allocating a new reserved block at the min capacity of `40`, bringing the total capacity to `500`.

The implicit assumption in this issue is that we want `reserve()` to be able to deallocate. No-one has objected to this before, but if you want to, please speak up.

Anyway, the Complexity of `reserve()` does not presently reflect this ability.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

An explicit objection was raised that the Effects: should state whether `reserve()` is allowed, or even required, to deallocate blocks.

Related to LWG 4323(i).

Proposed resolution:

This wording is relative to N5014.

1. Modify 23.3.9.3 [hive.capacity] as indicated:

   >     void reserve(size_type n);

   > > -3- Effects: If `n <= capacity()` is `true`, there are no effects. Otherwise increases `capacity()` by allocating reserved blocks.

   > > -4- Postconditions: `capacity() >= n` is `true`.

   > > -5- Throws: `length_error` if `n` `> max_size()`, as well as any exceptions thrown by the allocator.

   > > -6- Complexity: It does not change the size of the sequence and takes at most linear time in the number of reserved blocks allocated and deallocated.

   > > -7- Remarks: All references, pointers, and iterators referring to elements in `*this`, as well as the past-the-end iterator, remain valid.

### 4386(i). `std::simd::select(bool c, const T& a, const U& b)` is underconstrained

Section: 29.10.8.12 [simd.alg] Status: New Submitter: Hewill Kang Opened: 2025-09-27 Last modified: 2025-10-22

Priority: 3

View all issues with New status.

Discussion:

This function currently only requires that `c ? a : b` be a well-formed expression, which simply returns `c ? a : b`.

Given that it seems intended to work with `basic_vec`, `basic_mask`, or vectorizable type, requiring `T` and `U` to be copyable seems reasonable since they are trivially copyable. It shouldn't take non-copyable objects and produce hard errors in the function body.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

Split opinion between further pinning down exact condition (`is_constructible_v`, `decltype(auto(c ? a : b))`), and accepting the PR (`copyable`) as this is not meant to be generic facility.

Proposed resolution:

This wording is relative to N5014.

1. Modify 29.10.3 [simd.syn] as indicated:

   >     namespace std::simd {
   >       […]
   >       // 29.10.8.12 [simd.alg], algorithms
   >       […]
   >       template<copyableclass T, copyableclass U>
   >         constexpr auto select(bool c, const T& a, const U& b)
   >         -> remove_cvref_t<decltype(c ? a : b)>;
   >       […]
   >     };

2. Modify 29.10.8.12 [simd.alg] as indicated:

   >     template<copyableclass T, copyableclass U>
   >       constexpr auto select(bool c, const T& a, const U& b)
   >         -> remove_cvref_t<decltype(c ? a : b)>;

   > > -9- Effects: Equivalent to: `return c ? a : b;`

### 4389(i). `ranges::for_each` possibly behaves differently from range-based `for`

Section: 25.4.2 [range.range] Status: SG9 Submitter: Jiang An Opened: 2025-09-28 Last modified: 2025-10-23

Priority: 2

View all other issues in [range.range].

View all issues with SG9 status.

Discussion:

It was found in the blog post "When `ranges::for_each` behaves differently from `for`" that `ranges::for_each` can behave differently from range-based `for`, because

1. `ranges::begin` and `ranges::end` possibly use different rules, i.e. one calls a member and the other calls an ADL-found non-member function, and

2. these CPOs continue to perform ADL when a member `begin/end` is found but the function call is not valid, while the range-for stops and renders the program ill-formed.

Perhaps the intent of Ranges was that the `ranges::range` concept should be stricter than plain range-for and all range types can be iterated via range-for with the same semantics as `ranges::for_each`. However, it seems very difficult (if not impossible) for a library implementation to tell whether a class has member `begin/end` but the corresponding member call is ill-formed with C++20 core language rules, and such determination is critical for eliminating the semantic differences between `ranges::for_each` and range-for.

[2025-10-23; Reflector poll; Status changed: New → SG9 and P2.]

This is certainly evoluationary question and should go to LEWG/SG9. It would disallow having unrelated begin/end members, where the range interface is provided by hidden friends instead of those members.

Proposed resolution:

This wording is relative to N5014.

> Two mutually exclusive resolutions are proposed here. One enforces semantic-identity checks, while the other doesn't and makes weird types satisfy but not model the range concept. I prefer the stricter one because the semantic-identity checks are fully static, but this probably requires compilers to add new intrinsics when reflection is absent.

Option A: (stricter)

1. Modify 25.3.2 [range.access.begin] as indicated:

   > -2- Given a subexpression `E` with type `T`, let `t` be an lvalue that denotes the reified object for `E`. Then:

   > 1. (2.1) — If `E` is an rvalue and `enable_borrowed_range<remove_cv_t<T>>` is `false`, `ranges::begin(E)` is ill-formed.

   > 2. (2.2) — Otherwise, if `T` is an array type (9.3.4.5 [dcl.array]) and `remove_all_extents_t<T>` is an incomplete type, `ranges::begin(E)` is ill-formed with no diagnostic required.

   > 3. (2.3) — Otherwise, if `T` is an array type, `ranges::begin(E)` is expression-equivalent to `t + 0`.

   > 4. (2.4) — Otherwise, if `auto(t.begin())` is a valid expression whose type models `input_or_output_iterator`, `ranges::begin(E)` is expression-equivalent to `auto(t.begin())`.

   > 5. (2.?) — Otherwise, if `remove_cvref_t<T>` is a class type and search for `begin` in the scope of that class finds at least one declaration, `ranges::begin(E)` is ill-formed.

   > 6. (2.5) — Otherwise, if `T` is a class or enumeration type and `auto(begin(t))` is a valid expression whose type models `input_or_output_iterator` where the meaning of `begin` is established as-if by performing argument-dependent lookup only (6.5.4 [basic.lookup.argdep]), then `ranges::begin(E)` is expression-equivalent to that expression.

   > 7. (2.6) — Otherwise, `ranges::begin(E)` is ill-formed.

2. Modify 25.3.3 [range.access.end] as indicated:

   > -2- Given a subexpression `E` with type `T`, let `t` be an lvalue that denotes the reified object for `E`. Then:

   > 1. (2.1) — If `E` is an rvalue and `enable_borrowed_range<remove_cv_t<T>>` is `false`, `ranges::end(E)` is ill-formed.

   > 2. (2.2) — Otherwise, if `T` is an array type (9.3.4.5 [dcl.array]) and `remove_all_extents_t<T>` is an incomplete type, `ranges::end(E)` is ill-formed with no diagnostic required.

   > 3. (2.3) — Otherwise, if `T` is an array of unknown bound, `ranges::end(E)` is ill-formed.

   > 4. (2.4) — Otherwise, if `T` is an array, `ranges::end(E)` is expression-equivalent to `t + extent_v<T>`.

   > 5. (2.5) — Otherwise, if `auto(t.end())` is a valid expression whose type models `sentinel_for<iterator_t<T>>` then `ranges::end(E)` is expression-equivalent to `auto(t.end())`.

   > 6. (2.?) — Otherwise, if `remove_cvref_t<T>` is a class type and search for `end` in the scope of that class finds at least one declaration, `ranges::end(E)` is ill-formed.

   > 7. (2.6) — Otherwise, if `T` is a class or enumeration type and `auto(end(t))` is a valid expression whose type models `sentinel_for<iterator_t<T>>` where the meaning of end is established as-if by performing argument-dependent lookup only (6.5.4 [basic.lookup.argdep]), then `ranges::end(E)` is expression-equivalent to that expression.

   > 8. (2.7) — Otherwise, `ranges::end(E)` is ill-formed.

3. Modify 25.4.2 [range.range] as indicated:

   > -1- […]

   >     template<class T>
   >       concept range =
   >         requires(T& t) {
   >           ranges::begin(t);    // sometimes equality-preserving (see below)
   >           ranges::end(t);
   >         } && has-consistent-begin-end<T>; // see below

   > -2- […]

   > -3- […]

   > -?- `has-consistent-begin-end<T>` is a constant expression of type `bool`, and it is `true` if and only if for the `t` introduced in the requires-expression above, either

   > 1. (?.1) — both `ranges::begin(t)` and `ranges::end(t)` are specified to select `auto(t.begin())` and `auto(t.end())` respectively, or

   > 2. (?.2) — both `ranges::begin(t)` and `ranges::end(t)` are specified not to select `auto(t.begin())` and `auto(t.end())` respectively.

Option B: (looser)

1. Modify 25.4.2 [range.range] as indicated:

   > -1- […]

   >     template<class T>
   >       concept range =
   >         requires(T& t) {
   >           ranges::begin(t);    // sometimes equality-preserving (see below)
   >           ranges::end(t);
   >         }

   > -2- Given an expression `t` such that `decltype((t))` is `T&`, `T` models `range` only if

   > 1. (2.1) — […]

   > 2. (2.2) — […]

   > 3. (2.3) — […]

   > 4. (2.?) — The range-based `for` statement `for (auto&& x: t);` is well-formed, and variable definitions `auto begin = begin-expr;` and `auto end = end-expr;` in the equivalent form (8.6.5 [stmt.ranged]) of that statement are semantically equivalent to `auto begin = ranges::begin(t);` and `auto end = ranges::end(t);` respectively.

### 4392(i). `simd::unchecked_load` misses difference type casting

Section: 29.10.8.6 [simd.loadstore] Status: New Submitter: Hewill Kang Opened: 2025-09-29 Last modified: 2025-10-22

Priority: 3

View other active issues in [simd.loadstore].

View all other issues in [simd.loadstore].

View all issues with New status.

Discussion:

Currently, `simd::unchecked_load`/`partial_load`/`unchecked_store`/`partial_store` construct a `span` via `span(first, n)` when taking an iterator `first` and its difference type `n`.

However, the construction is ill-formed when the difference type is an integer-class type; we should perform an explicit casting here.

[2025-10-22; Reflector poll.]

Set priority to 3 after reflector poll.

Question was raised if we should use `size_t` instead of `iter_difference_t` for consistency with `span`.

Proposed resolution:

This wording is relative to N5014.

1. In subclause 29.10.8.6 [simd.loadstore] replace all occurrences of

   >     R(first, n)

   by

   >     R(first, static_cast<size_t>(n))

### 4394(i). `simd::unchecked_load(I first, S last)` construct `span` maybe ill-formed

Section: 29.10.8.6 [simd.loadstore] Status: New Submitter: Hewill Kang Opened: 2025-09-30 Last modified: 2025-10-17

Priority: 3

View other active issues in [simd.loadstore].

View all other issues in [simd.loadstore].

View all issues with New status.

Discussion:

Currently, `simd::unchecked_load`/`partial_load`/`unchecked_store`/`partial_store` constructs a `span` via `span(first, last)` when taking an iterator-sentinel pair `first` and `last`.

However, the construction may not be well-behaved or well-formed when the sentinel type can be implicitly converted to an integer type. Consider:

>     struct I {
>       using value_type = int;
>       using difference_type = int;
>       using iterator_category = std::contiguous_iterator_tag;
>       // contiguous iterator operators
>       // ...

>       operator int() const;
>     };

>     int main() {
>        std::simd::unchecked_load(I{}, I{});
>     }

Above, `unchecked_load` invokes `unchecked_load(I first, S last)` and we attempt to construct `span` through `span(first, last)`. However, this is invalid because the constructor requires that the sentinel type should not be convertible to `size_t`, so we fall back into `span(first, n)` via implicitly converting `I` to `size_t`. Such behavior is subtle and likely unintended.

Now consider:

>     struct I {
>       using value_type = int;
>       using difference_type = int;
>       using iterator_category = std::contiguous_iterator_tag;
>       // contiguous iterator operators
>       // ...

>       operator int() &&;
>     };

>     int main() {
>        std::simd::unchecked_load(I{}, I{});
>     }

We still attempt to construct the `span` by calling `span(first, last)`, which is invalid, but because the lvalue sentinel cannot be converted to `size_t`, the call of `span(first, n)` is also invalid. This makes the construction of the `span` ill-formed and leads to a hard error in the function body.

[2025-10-17; Reflector poll.]

Set priority to 3 after reflector poll.

"`unchecked_load(first, last)` needs to be equivalent to `unchecked_load(span(first, last))`. If `span` converts `last` to `size_t` then simd should do the same, not `span(first, last-first)`. Should simd (first,last) overloads have the same `is_convertible_v<End, size_t>` is `false` constraint as `span`?"

Proposed resolution:

This wording is relative to N5014.

1. In subclause 29.10.8.6 [simd.loadstore] replace all occurrences of

   >     R(first, last)

   by

   >     R(first, static_cast<size_t>(last - first))

### 4395(i). `write_env` implementation-detail lambda should have explicit return type

Section: 33.9.12.3 [exec.write.env] Status: New Submitter: Robert A.H. Leahy Opened: 2025-09-29 Last modified: 2025-10-23

Priority: 2

View all issues with New status.

Discussion:

In 33.9.12.3 [exec.write.env] the impls for `std::execution::write_env` has `get-env` specified as:

>     static constexpr auto get-env =
>       [](auto, const auto& state, const auto& rcvr) noexcept {
>         return see-below;
>       };

This uses automatic return type deduction which means that the body of the lambda is actually instantiated in a SFINAE-unfriendly way when attempting to compute various properties of the lambda (invocability, return type, et cetera). This is undesirable and surprising as has been discovered in actual use/deployment (see: https://github.com/NVIDIA/stdexec/pull/1654).

The fix is to explicitly provide a return type for the lambda which computes the type of the body.

Note: This issue may indicate that the use of automatic return type deduction in the specification of sender algorithms more generally ought to be reconsidered/-examined.

[2025-10-23; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

### 4397(i). Improve `span(R&& r)`

Section: 23.7.2.2.2 [span.cons] Status: Ready Submitter: Hewill Kang Opened: 2025-10-02 Last modified: 2026-06-09

Priority: 3

View all other issues in [span.cons].

View all issues with Ready status.

Discussion:

It is preferable to reject `span<int, 42>(views::empty<int>)` at compile-time after P2280R4, since applying `ranges::size` on those ranges is a constant expression now.

[2025-10-20; Reflector poll.]

Set priority to 3 after reflector poll.

The opinions on reflector discussion where split regarding, if this should be considered LEWG matter.

Question was raised, if `ranges::size(r) == N` is required to be usable at compile-time for integer-class types.

Previous resolution [SUPERSEDED]

This wording is relative to N5014.

1. Modify 23.7.2.2.2 [span.cons] as indicated:

   >     template<class R> constexpr explicit(extent != dynamic_extent) span(R&& r);

   > > -?- Mandates: If `extent` is not equal to `dynamic_extent` and `ranges::size(r)` is a constant expression, then `ranges::size(r) == extent` is `true`.

   > > -16- Constraints: Let `U` be `remove_reference_t<ranges::range_reference_t<R>>`. […]

[2026-06-09; Jonathan provides new wording]

Move Mandates: after Constraints: and add more context.

[Brno 2026-06-09; Change status New → Ready]

Proposed resolution:

This wording is relative to N5046.

1. Modify 23.7.2.2.2 [span.cons] as indicated:

   >     template<class R> constexpr explicit(extent != dynamic_extent) span(R&& r);

   > > -16- Constraints: Let `U` be `remove_reference_t<ranges::range_reference_t<R>>`. […]

   > > -?- Mandates: If `extent` is not equal to `dynamic_extent` and `ranges::size(r)` is a constant expression, then `ranges::size(r) == extent` is `true`.

   > > -17- Preconditions: […]

   > > -18- Hardened preconditions: If `extent` is not equal to `dynamic_extent`, then `ranges::size(r) == extent` is `true`.

### 4400(i). `enable_nonlocking_formatter_optimization` for durations with custom rep

Section: 30.12 [time.format] Status: New Submitter: Tomasz Kamiński Opened: 2025-10-02 Last modified: 2025-10-14

Priority: 3

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with New status.

Discussion:

Currently the `enable_nonlocking_formatter_optimization` is enabled for `duration<Rep, Ratio>` if it is enabled for `Rep`.

>     template<class Rep, class Period>
>       constexpr bool enable_nonlocking_formatter_optimization<chrono::duration<Rep, Period>>
>         =  enable_nonlocking_formatter_optimization<Rep>;

However, this does not take into the consideration that for custom `Rep` types, the arithmetic operations on `Rep` may also lock the stream leading to deadlock (for example log on overflow). Since they are required to handle the specifiers such as `%S` we should specialize `enable_nonlocking_formatter_optimization` only for built-in types:

>     template<class Rep, class Period>
>       constexpr bool enable_nonlocking_formatter_optimization<chrono::duration<Rep, Period>>
>         =  is_arithmetic_v<Rep>;

Furthermore, for all types that are currently templated on `Duration` (`hh_mm_ss`, `sys_time`, `local_time`, etc.), we `enable_nonlocking_formatter_optimization` by default. This again does not take into consideration the arithmetic operations performed as duration. We should specialize `enable_nonlocking_formatter_optimization` for all of them to be enabled if `enable_nonlocking_formatter_optimization` is enabled for `duration`:

>     template<class Duration>
>       constexpr bool enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<Duration>>
>         =  enable_nonlocking_formatter_optimization<Duration>;

Note, that forwarding to `enable_nonlocking_formatter_optimization` on `Duration` instead of checking `Duration::rep` allows users to specialize `enable_nonlocking_formatter_optimization` for Durations with there custom representation types.

The proposed wording has recently been implemented in gcc's libstdc++.

[2025-10-14; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N5014.

1. Modify 30.12 [time.format] as indicated:

   > -8- For `chrono::duration`, `chrono::hh_mm_ss`, `chrono::sys_time`, `chrono::utc_time`, `chrono::tai_time`, `chrono::gps_time`, `chrono::file_time`, `chrono::local_time`, `chrono::local-time-format-t`, and `chrono::zoned_time` the library only provides the following specializations of `enable_nonlocking_formatter_optimization`:

   > >     template<class Rep, class Period>
   > >       constexpr bool enable_nonlocking_formatter_optimization<
   > >         chrono::duration<Rep, Period>> =
   > >           enable_nonlocking_formatter_optimizationis_arithmetic_v<Rep>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::sys_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::utc_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::tai_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::gps_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::file_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::local_time<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<chrono::local-time-format-t<Duration>>
   > >          = enable_nonlocking_formatter_optimization<Duration>;

   > -9- For `chrono::zoned_time` the library only provides the following specialization of `enable_nonlocking_formatter_optimization`:

   > >     template<class Duration>
   > >       constexpr bool enable_nonlocking_formatter_optimization<
   > >         chrono::zoned_time<Duration, const std::chrono::time_zone*>>
   > >           = trueenable_nonlocking_formatter_optimization<Duration>;

### 4401(i). `join_view` should be `sized_range` when applied to ranges of `simd::vec`

Section: 25.7.14.2 [range.join.view], 25.7.15.2 [range.join.with.view], 25.7.16.2 [range.lazy.split.view] Status: LEWG Submitter: Hewill Kang Opened: 2025-10-02 Last modified: 2025-10-23

Priority: 3

View all other issues in [range.join.view].

View all issues with LEWG status.

Discussion:

Consider:

Collecting a `simd::vec` into a `vector` for output is a common use case. P3480R6 makes `simd::vec` a range so we can simply flatten it with `views::join` (original example from the paper):

>     std::vector<std::simd::vec<float>> data;
>     auto range_of_float = data | std::views::join;

In this case, it makes sense for `join_view` to be `sized_range` because `simd::vec::size()` is a constant expression that can be multiplied by the original `vector` size to get the result size of the `join_view`.

In `<ranges>`, we use the `tiny-range` concept to consider types that can obtain static sizes specifically, and `simd::vec` seems to be a good fit.

[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]

Introducting statically sized ranges and handling them in views, should be handle as a paper.

The proposed change is also relevant to fixed-size `span`.

Proposed resolution:

This wording is relative to N5014.

1. Modify 25.7.14.2 [range.join.view] as indicated:

   > [Drafting note: The proposed wording follows the `tiny-range`'s way to check if `R::size()` is a constant expression instead of further checking `ranges::size(r)` for simplicity.]

   >     namespace std::ranges {
   >       template<auto> struct require-constant;  // exposition only

   >       template<class R>
   >       concept static-sized-range =             // exposition only
   >         sized_range<R> &&
   >         requires { typename require-constant<remove_reference_t<R>::size()>; };

   >       template<input_range V>
   >         requires view<V> && input_range<range_reference_t<V>>>
   >       class join_view : public view_interface<join_view<V>> {
   >       […]
   >       public:
   >         […]
   >         constexpr auto size()
   >           requires sized_range<V> && static-sized-range<InnerRng> {
   >           using CT = common_type_t<range_size_t<V>, range_size_t<InnerRng>>;
   >           return CT(ranges::size(base_)) * CT(remove_reference_t<InnerRng>::size());
   >         }

   >         constexpr auto size() const
   >           requires sized_range<const V> &&
   >                    static-sized-range<range_reference_t<const V>> {
   >           using InnerConstRng = range_reference_t<const V>;
   >           using CT = common_type_t<range_size_t<V>, range_size_t<InnerConstRng>>;
   >           return CT(ranges::size(base_)) * CT(remove_reference_t<InnerConstRng>::size());
   >         }
   >       };
   >       […]
   >     }

2. Modify 25.7.15.2 [range.join.with.view] as indicated:

   >     namespace std::ranges {
   >       […]
   >       template<input_range V, forward_range Pattern>
   >         requires view<V> && input_range<range_reference_t<V>>
   >               && view<Pattern>
   >               && concatable<range_reference_t<V>, Pattern>
   >       class join_with_view : public view_interface<join_with_view<V, Pattern>> {
   >       […]
   >       public:
   >         […]
   >         constexpr auto size()
   >           requires sized_range<V> && sized_range<Pattern> &&
   >                    static-sized-range<InnerRng> {
   >           using CT = common_type_t<
   >             range_size_t<V>, range_size_t<InnerRng>, range_size_t<Pattern>>;
   >           const auto base_size = ranges::size(base_);
   >           if (base_size == 0)
   >             return CT(0);
   >           return CT(base_size) * CT(remove_reference_t<InnerRng>::size()) +
   >                  CT(base_size - 1) * CT(ranges::size(pattern_));
   >         }

   >         constexpr auto size() const
   >           requires sized_range<const V> && sized_range<const Pattern> &&
   >                    static-sized-range<range_reference_t<const V>> {
   >           using InnerConstRng = range_reference_t<const V>;
   >           using CT = common_type_t<
   >             range_size_t<const V>, range_size_t<InnerConstRng>, range_size_t<const Pattern>>;
   >           const auto base_size = ranges::size(base_);
   >           if (base_size == 0)
   >             return CT(0);
   >           return CT(base_size) * CT(remove_reference_t<InnerConstRng>::size()) +
   >                  CT(base_size - 1) * CT(ranges::size(pattern_));
   >         }
   >       };
   >       […]
   >     }

3. Modify 25.7.16.2 [range.lazy.split.view] as indicated:

   >     namespace std::ranges {
   >       template<auto> struct require-constant;                       // exposition only

   >       template<class R>
   >       concept tiny-range =                                          // exposition only
   >         static-sized-range<R>sized_range<R> &&
   >         requires { typename require-constant<remove_reference_t<R>::size()>; } &&
   >         (remove_reference_t<R>::size() <= 1);

   >       […]
   >     }

### 4405(i). `mdspan` constructor should disallow derived to base conversions

Section: 23.7.3.6.2 [mdspan.mdspan.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-05 Last modified: 2025-10-23

Priority: 3

View all issues with New status.

Discussion:

Unlike `ranges::subrange` or `span`, `mdspan` syntactically allows a multidimensional viewing base class via a derived class pointer (demo):

>     #include <span>
>     #include <ranges>
>     #include <mdspan>

>     struct Base {};
>     struct Derived : Base {};
>     std::array<Derived, 12> arr;
>     std::ranges::subrange<Base*> s(arr); // error, slicing
>     std::span<Base> sp(arr.data(), arr.size()); // error, slicing
>     std::mdspan<Base, std::dims<1>> msp(arr.data(), arr.size()); // ok

Given that we intend to reject object slicing for both `default_accessor` and `aligned_accessor`, there seems no reason not to reject this invalid pointer arithmetic for `mdspan`.

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

Even if `data_handle_type` is a pointer to `value_type`, this does not mean that it points to contiguous range of `value_type` objects. Accessor may provide different access parttern.

Proposed resolution:

This wording is relative to N5014.

> [Drafting note: The exposition-only concept `convertible-to-non-slicing` comes from 25.5.4.1 [range.subrange.general].]

1. Modify 23.7.3.6.1 [mdspan.mdspan.overview] as indicated:

   >     namespace std {
   >       template<class ElementType, class Extents, class LayoutPolicy = layout_right,
   >                class AccessorPolicy = default_accessor<ElementType>>
   >       class mdspan {
   >       public:
   >         using extents_type = Extents;
   >         using layout_type = LayoutPolicy;
   >         using accessor_type = AccessorPolicy;
   >         using mapping_type = typename layout_type::template mapping<extents_type>;
   >         using element_type = ElementType;
   >         using value_type = remove_cv_t<element_type>;
   >         using index_type = typename extents_type::index_type;
   >         using size_type = typename extents_type::size_type;
   >         using rank_type = typename extents_type::rank_type;
   >         using data_handle_type = typename accessor_type::data_handle_type;
   >         using reference = typename accessor_type::reference;
   >         […]
   >         template<class... OtherIndexTypes>
   >           constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto ptr, OtherIndexTypes... exts);
   >         template<class OtherIndexType, size_t N>
   >           constexpr explicit(N != rank_dynamic())
   >             mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts);
   >         template<class OtherIndexType, size_t N>
   >           constexpr explicit(N != rank_dynamic())
   >             mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);
   >         constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);
   >         constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);
   >         constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);
   >         […]
   >       };
   >       […]
   >     }

2. Modify 23.7.3.6.2 [mdspan.mdspan.cons] as indicated:

   >     template<class... OtherIndexTypes>
   >       constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto p, OtherIndexTypes... exts);

   > > -4- Let `N` be `sizeof...(OtherIndexTypes)`.

   > > -5- Constraints: […]

   > > […]

   >     template<class OtherIndexType, size_t N>
   >       constexpr explicit(N != rank_dynamic())
   >         mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts);
   >     template<class OtherIndexType, size_t N>
   >       constexpr explicit(N != rank_dynamic())
   >         mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);

   > > -8- Constraints: […]

   > > […]

   >     constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);

   > > -11- Constraints: […]

   > > […]

   >     constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);

   > > -14- Constraints: […]

   > > […]

   >     constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);

   > > -17- Preconditions: […]

   > > […]

### 4406(i). `optional::value_or` return statement is inconsistent with Mandates

Section: 22.5.3.7 [optional.observe], 22.5.4.6 [optional.ref.observe], 22.8.6.6 [expected.object.obs] Status: Ready Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2026-06-10

Priority: 3

View other active issues in [optional.observe].

View all other issues in [optional.observe].

View all issues with Ready status.

Discussion:

`optional<T>::value_or(U&&)` requires `is_convertible_v<U&&, T>` to ensure that `T` can be convert from `U` when `optional` has no value.

However, the return statement explicitly constructs `T` by `static_cast`, which is not checked by `is_convertible_v` since it only checks for implicit conversions.

This results in rare cases where Mandates may not be violated, but `value_or` is ill-formed (demo):

>     struct S {
>       operator int() const;
>       explicit operator int() = delete;
>     };

>     int main() {
>        std::optional<int>{}.value_or(S{}); // fire
>     }

It is reasonable to create objects that stick to Mandates. The same goes for `expected::value_or`.

Daniel:

This issue has considerable overlap with LWG 4281(i).

[2025-10-16; Reflector poll]

Set priority to 3 after reflector poll.

This would need to use `val` instead of `**this` if LWG 4015(i) is accepted.

Previous resolution [SUPERSEDED]

This wording is relative to N5014.

1. Modify 22.5.3.7 [optional.observe] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;

   > > -15- Mandates: `is_copy_constructible_v<T> && is_convertible_v<U&&, T>` is `true`.

   > > -16- Effects: Equivalent to:

   > > >     return has_value() ? **this : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return **this;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;

   > > -17- Mandates: `is_move_constructible_v<T> && is_convertible_v<U&&, T>` is `true`.

   > > -18- Effects: Equivalent to:

   > > >     return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return std::move(**this);
   > > >     return std::forward<U>(v);

2. Modify 22.5.4.6 [optional.ref.observe] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;

   > > -8- Let `X` be `remove_cv_t<T>`.

   > > -9- Mandates: `is_constructible_v<X, T&> && is_convertible_v<U, X>` is `true`.

   > > -10- Effects: Equivalent to:

   > > >     return has_value() ? *val : static_cast<X>(std::forward<U>(u));
   > > >     if (has_value())
   > > >       return *val;
   > > >     return std::forward<U>(u);

3. Modify 22.8.6.6 [expected.object.obs] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;

   > > -18- Mandates: `is_copy_constructible_v<T>` is `true` and `is_convertible_v<U, T>` is `true`.

   > > -19- Returns: `has_value() ? **this : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return **this;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;

   > > -20- Mandates: `is_move_constructible_v<T>` is `true` and `is_convertible_v<U, T>` is `true`.

   > > -21- Returns: `has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return std::move(**this);
   > > >     return std::forward<U>(v);

[2025-11-11; Jonathan provides updated wording]

Rebase after LWG 4015(i) which was approved in Kona. This would also resolve LWG 4281(i).

[2025-11-19; Jonathan provides updated wording]

Incorporate proposed resolution of 3424(i) as well, so that the return type is never cv-qualified.

Previous resolution [SUPERSEDED]

This wording is relative to N5032.

1. Modify 22.5.3.1 [optional.optional.general] as indicated:

   >     […]
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &;
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

2. Modify 22.5.3.7 [optional.observe] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -15- Mandates: `is_copy_constructible_v<T> is_convertible_v<const T&, X> && is_convertible_v<U&&, T X>` is `true`.

   > > -16- Effects: Equivalent to:

   > > >     return has_value() ? val : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return val;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -17- Mandates: `is_move_constructible_v<T> is_convertible_v<T, X> && is_convertible_v<U&&, T X>` is `true`.

   > > -18- Effects: Equivalent to:

   > > >     return has_value() ? std::move(val) : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return std::move(val);
   > > >     return std::forward<U>(v);

3. Modify 22.5.4.6 [optional.ref.observe] as indicated:

   >     constexpr T& value() const;

   > > -7- Effects: Equivalent to:

   > > >     return has_value() ? *val : throw bad_optional_access();
   > > >     if (has_value())
   > > >       return *val;
   > > >     throw bad_optional_access();

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;

   > > -8- Let `X` be `remove_cv_t<T>`.

   > > -9- Mandates: `is_constructible_v<T> is_convertible_v<T&, X> && is_convertible_v<U, X>` is `true`.

   > > -10- Effects: Equivalent to:

   > > >     return has_value() ? *val : static_cast<X>(std::forward<U>(u));
   > > >     if (has_value())
   > > >       return *val;
   > > >     return std::forward<U>(u);

4. Modify 22.8.6.1 [expected.object.general] as indicated:

   >     […]
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &;
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

5. Modify 22.8.6.6 [expected.object.obs] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -18- Mandates: `is_copy_constructible_v<T>` is `true` and `is_convertible_v<const T&, X> &&` `is_convertible_v<U, T>` is `true`.

   > > -19- Returns: `has_value() ? **this : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return val;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -20- Mandates: `is_move_constructible_v<T>` is `true` and `is_convertible_v<T, X> &&` `is_convertible_v<U, T>` is `true`.

   > > -21- Returns: `has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return std::move(val);
   > > >     return std::forward<U>(v);

[Brno 2026-06-09; Jonathan provides new wording]

Fix two `T` that should be `X` and fix incorrect deleted context in [optional.ref.observe].

[Brno 2026-06-10; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5046.

1. Modify 22.5.3.1 [optional.optional.general] as indicated:

   >     […]
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &;
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

2. Modify 22.5.3.7 [optional.observe] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -15- Mandates: `is_copy_constructible_v<T> is_convertible_v<const T&, X> && is_convertible_v<U&&, T X>` is `true`.

   > > -16- Effects: Equivalent to:

   > > >     return has_value() ? val : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return val;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -17- Mandates: `is_move_constructible_v<T> is_convertible_v<T, X> && is_convertible_v<U&&, T X>` is `true`.

   > > -18- Effects: Equivalent to:

   > > >     return has_value() ? std::move(val) : static_cast<T>(std::forward<U>(v));
   > > >     if (has_value())
   > > >       return std::move(val);
   > > >     return std::forward<U>(v);

3. Modify 22.5.4.6 [optional.ref.observe] as indicated:

   >     constexpr T& value() const;

   > > -7- Effects: Equivalent to:

   > > >     return has_value() ? *val : throw bad_optional_access();
   > > >     if (has_value())
   > > >       return *val;
   > > >     throw bad_optional_access();

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;

   > > -8- Constraints: `T` is a non-array type.

   > > -9- Let `X` be `remove_cv_t<T>`.

   > > -10- Mandates: `is_constructible_v<X, T&> is_convertible_v<T&, X> && is_convertible_v<U, X>` is `true`.

   > > -11- Effects: Equivalent to:

   > > >     return has_value() ? *val : static_cast<X>(std::forward<U>(u));
   > > >     if (has_value())
   > > >       return *val;
   > > >     return std::forward<U>(u);

   > > -12- Remarks: The return type is unspecified if `T` is an array type or a non-object type.

4. Modify 22.8.6.1 [expected.object.general] as indicated:

   >     […]
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &;
   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&;
   >     […]

5. Modify 22.8.6.6 [expected.object.obs] as indicated:

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -18- Mandates: `is_copy_constructible_v<T>` is `true` and `is_convertible_v<const T&, X> &&` `is_convertible_v<U, T X>` is `true`.

   > > -19- Returns: `has_value() ? **this : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return val;
   > > >     return std::forward<U>(v);

   >     template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;

   > > -?- Let `X` be `remove_cv_t<T>`.

   > > -20- Mandates: `is_move_constructible_v<T>` is `true` and `is_convertible_v<T, X> &&` `is_convertible_v<U, T X>` is `true`.

   > > -21- Returns: `has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v))`.

   > > -?- Effects: Equivalent to:

   > > >     if (has_value())
   > > >       return std::move(val);
   > > >     return std::forward<U>(v);

### 4408(i). Hardening `simd::vec::operator[]`

Section: 29.10.7.3 [simd.subscr], 29.10.9.3 [simd.mask.subscr] Status: LEWG Submitter: Hewill Kang Opened: 2025-10-07 Last modified: 2025-10-22

Priority: 3

View all issues with LEWG status.

Discussion:

`simd::vec::operator[]` satisfies the criteria in P3471R4 and P3697R1 : "violating the precondition results in a memory safety issue", which means that hardening is reasonable.

[2025-10-22; Reflector poll. Status changed: New → LEWG and P3.]

Proposed resolution:

This wording is relative to N5014.

1. Modify 29.10.7.3 [simd.subscr] as indicated:

   >     constexpr value_type operator[](simd-size-type i) const;

   > > -1- Hardened Ppreconditions: `i >= 0 && i < size()` is `true`.

2. Modify 29.10.9.3 [simd.mask.subscr] as indicated:

   >     constexpr value_type operator[](simd-size-type i) const;

   > > -1- Hardened Ppreconditions: `i >= 0 && i < size()` is `true`.

### 4409(i). Constant expression `ranges::size(r)` Constraints and Mandates in [simd]

Section: 29.10 [simd] Status: New Submitter: Lénárd Szolnoki Opened: 2025-10-10 Last modified: 2025-10-22

Priority: 3

View all other issues in [simd].

View all issues with New status.

Discussion:

Various Constraints and Mandates in 29.10 [simd] are conditioned on `ranges::size(r)`, assuming that subsequent operations on the result are also constant expression. This is not a given, since `ranges::size(r)` is allowed to return an integer-class type, and it is not mandated that operations on integer-class types are usable in constant expressions.

In particular:

1. 29.10.7.2 [simd.ctor] p13:

   > Constraints:

   > - `R` models `ranges::contiguous_range` and `ranges::sized_range`,

   > - `ranges::size(r)` is a constant expression, and

   > - `ranges::size(r)` is equal to `size()`.

   Here `ranges::size(r) == size()` might not be a constant expression, even when `ranges::size(r)` is. Operations that are not accounted for:

   - Possible conversion from `range::size(r)` to `simd-size-type`.

   - Possible operator overloading on ==.

2. 29.10.7.2 [simd.ctor] p17+18:

   > Constraints:

   > - `R` models `ranges::contiguous_range` and `ranges::sized_range`, and

   > - `ranges::size(r)` is a constant expression

   > Remarks: The deduced type is equivalent to `vec<ranges::range_value_t<R>, ranges::size(r)>`.

   Here the second constraint is simply redundant, if failure to form the type is in the immediate context. If we want to type it out in the constraints then we should account for conversion to `simd-size-type` and narrowing.

3. 29.10.8.6 [simd.loadstore] p2:

   > Mandates: If `ranges::size(r)` is a constant expression then `ranges::size(r)` ≥ `V::size()`.

   Here `ranges::size(r) >= V::size()` might not be a constant expression, even when `ranges::size(r)` is. It is unclear why a mathematical operator symbol is used here (and further down in the preconditions).

[2025-10-22; Reflector poll.]

Set priority to P3 after reflector poll.

General preference on considering this NAD, and instead clarifying that operations on integer-class should produce constant expressions.

Proposed resolution:

This wording is relative to N5014.

1. Modify 29.10.7.2 [simd.ctor] as indicated:

   >     template<class R, class... Flags>
   >       constexpr basic_vec(R&& r, flags<Flags...> = {});
   >     template<class R, class... Flags>
   >       constexpr basic_vec(R&& r, const mask_type& mask, flags<Flags...> = {});

   > > -12- Let `mask` be `mask_type(true)` for the overload with no `mask` parameter.

   > > -13- Constraints:

   > > 1. (13.1) — `R` models `ranges::contiguous_range` and `ranges::sized_range`, and

   > > 2. (13.2) — `ranges::size(r) == size()` is a constant expression, and evaluates to `true`.

   > > 3. (13.3) — `ranges::size(r)` is equal to `size()`.

   > > […]

   >     template<class R, class... Ts>
   >       basic_vec(R&& r, Ts...) -> see below;

   > > -17- Constraints:

   > > 1. (17.1) — `R` models `ranges::contiguous_range` and `ranges::sized_range`, and.

   > > 2. (17.2) — `ranges::size(r)` is a constant expression.

   > > -18- Remarks: The deduced type is equivalent to `vec<ranges::range_value_t<R>, ranges::size(r)>`.

2. Modify 29.10.8.6 [simd.loadstore] as indicated:

   >     template<class V = see below, ranges::contiguous_range R, class... Flags>
   >       requires ranges::sized_range<R>
   >       constexpr V unchecked_load(R&& r, flags<Flags...> f = {});
   >     […]
   >     template<class V = see below, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>
   >       constexpr V unchecked_load(I first, S last, const typename V::mask_type& mask,
   >                                  flags<Flags...> f = {});

   > > -1- Let […]

   > > -2- Mandates: If `ranges::size(r) >= V::size()` is a constant expression then `ranges::size(r)` ≥ `V::size()`it evaluates to `true`.

### 4410(i). `basic_string_view(It begin, End end)` is underconstrained

Section: 27.3.3.2 [string.view.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-12 Last modified: 2025-10-17

Priority: 4

View other active issues in [string.view.cons].

View all other issues in [string.view.cons].

View all issues with New status.

Discussion:

The following lead to a hard error in the `string_view` constructor (demo):

>     volatile char* p;
>     std::string_view sv(p, p); // error: invalid conversion from 'volatile char*' to 'const char*'

Because the constructor currently only requires that the value type of the contiguous iterator be `char`, which does not reject `volatile char*`, which fires when further initializing `const char*`.

[2025-10-17; Reflector poll.]

Set priority to 4 after reflector poll.

"NAD, don't care about volatile." "Maybe make it Mandates: instead of Constraints:." "We already reject `span<const char> s(p,p)` so should also reject `string_view sv(p,p)`."

Proposed resolution:

This wording is relative to N5014.

1. Modify 27.3.3.2 [string.view.cons] as indicated:

   >     template<class It, class End>
   >       constexpr basic_string_view(It begin, End end);

   > > -7- Constraints:

   > > 1. (7.1) — `It` satisfies `contiguous_iterator`.

   > > 2. (7.2) — `End` satisfies `sized_sentinel_for<It>`.

   > > 3. (7.3) — `is_same_v<iter_value_t<It>, charT>` is `true`.

   > > 4. (7.?) — `is_convertible_v<add_pointer_t<iter_reference_t<It>>, const_pointer>` is `true`.

   > > 5. (7.4) — `is_convertible_v<End, size_type>` is `false`.

   > > […]

   >     template<class R>
   >       constexpr explicit basic_string_view(R&& r);

   > > -11- Let `d` be an lvalue of type `remove_cvref_t<R>`.

   > > -12- Constraints:

   > > 1. (12.1) — `remove_cvref_t<R>` is not the same type as `basic_string_view`,

   > > 2. (12.2) — `R` models `ranges::contiguous_range` and `ranges::sized_range`,

   > > 3. (12.3) — `is_same_v<ranges::range_value_t<R>, charT>` is `true`,

   > > 4. (12.?) — `is_convertible_v<add_pointer_t<ranges::range_reference_t<R>>, const_pointer>` is `true`,

   > > 5. (12.4) — `is_convertible_v<R, const charT*>` is `false`, and

   > > 6. (12.5) — `d.operator ::std::basic_string_view<charT, traits>()` is not a valid expression.

### 4411(i). Even more feature-test macros for fully freestanding features are not marked freestanding

Section: 17.3.2 [version.syn] Status: New Submitter: Jonathan Wakely Opened: 2025-10-14 Last modified: 2025-10-22

Priority: 2

View other active issues in [version.syn].

View all other issues in [version.syn].

View all issues with New status.

Discussion:

During the review of LWG 4286(i) it was noticed that the proposed resolution did not include a change to `__cpp_lib_ranges_indices`, which was added in Sofia by P3060R3 (which was after the issue was opened). It also doesn't change `__cpp_lib_constexpr_utility`, and probably should do. It doesn't change `__cpp_lib_tuple_like`, but that's less clear whether it should do. The relevant changes in `<tuple>` and `<pair>` are obviously available in a freestanding implementation, but the changes in `<map>` and `<unordered_map>` are not.

[2025-10-22; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

### 4417(i). `views::indices` is underconstrained

Section: 25.6.4.1 [range.iota.overview] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2025-10-15 Last modified: 2026-01-16

Priority: Not Prioritized

View all other issues in [range.iota.overview].

View all issues with Tentatively NAD status.

Discussion:

Whether an integer-class type satisfies `weakly_incrementable` is unspecified according to 25.6.4.2 [range.iota.view]. For example, the library may provide a member type alias `difference_type` for the integer-class type to make it `weakly_incrementable`, or not.

If the latter, then `views::iota(integer-class-type(0))` is always ill-formed because `iota_view<W, Bound>` requires `W` to be `weakly_incrementable`.

However, unlike `views::iota`, `views::indices` unconditionally accepts integer-class types, which will lead to a hard error in the function body if the integer-class type is not `weakly_incrementable`, which is true for both libstdc++ and MSVC-STL.

[2026-01-16; Reflector poll. Status → Tentatively NAD.]

"This is implied by the expression-equivalence."

Proposed resolution:

This wording is relative to N5014.

1. Modify 25.6.4.1 [range.iota.overview] as indicated:

   > -4- The name `views::indices` denotes a customization point object (16.3.3.3.5 [customization.point.object]). Given subexpression `E`, let `T` be `remove_cvref_t<decltype((E))>`. `views::indices(E)` is expression-equivalent to `views::iota(T(0), E)` if `is-integer-like<T>` is `true` and `T` models `weakly_incrementable`, and ill-formed otherwise.

### 4418(i). `co_yield elements_of(vector<int>())` does not compile

Section: 25.8.5 [coro.generator.promise] Status: New Submitter: Mathias Stearn Opened: 2025-10-16 Last modified: 2025-10-23

Priority: 2

View all other issues in [coro.generator.promise].

View all issues with New status.

Discussion:

The std::generator proposal (P2502) explicitly says that the following example is supposed to work, but it does not compile:

> For convenience, we further propose that `co_yield elements_of(x)` be extended to support yielding the values of arbitrary ranges beyond generators, ie

> >     std::generator<int> f()
> >     {
> >       std::vector<int> v = /*... */;
> >       co_yield std::ranges::elements_of(v);
> >     }

This doesn't compile because the overload listed in 25.8.5 [coro.generator.promise] p13 requires `convertible_to<ranges::range_reference_t<R>, yielded>` (i.e. `convertible_to<int&, int&&>`) which isn't satisfied.

So it seems clear enough that something got lost in the translation from the design intent to wording. My understanding of the intent is that `co_yield elements_of(rng)` should be semantically similar to the following code, but require a specific optimization in the case where `rng` is the same type of generator as the current coroutine:

>     for (auto&& x : rng) {
>       co_yield std::forward<decltype(x)>(x);
>     }

Note that that code does compile correctly where `rng` is a `vector<int>`, so I think the original code should as well.

Speculation on how this happened: Prior to P2529, the `generator<T>` in P2502R0 was basically the same as `generator<const T&>` where `co_yield elements_of(vector<int>())` does work. After P2529 (which was a design paper w/o wording) was approved by LEWG, P2502R1 was updated to change the reference type to `T&&`. However, when making the change, I assume that the impact it had on the `elements_of`-non-generator overload of `yield_value` was not considered, so it was unmodified.

[2025-10-23; Reflector poll.]

Set priority to 2 after reflector poll.

The current resolution is incorrect, and breaks following example:

>     std::generator<std::vector<int>> g()
>     {
>       std::vector<int> v = {1, 2, 3};
>       co_yield std::ranges::elements_of(v);
>     }

The constraints are equivalent to checking if `yield_value(*it)` is well-formed, and maybe we could express is directly as:

>     requires requires (promise_type p, ranges::iterator_t<R> it) {
>         p.yield_value(*it);
>     }

Proposed resolution:

This wording is relative to N5014.

1. Modify 25.8.5 [coro.generator.promise] as indicated:

   > [Drafting note: The following changes would check that `co_yield *i` would match one of the overloads of `yield_value()` described in paragraphs 4 or 6. Alternatively this could be expressed more directly as a helper comment or in prose in a Mandates: element.

   > >     namespace std {
   > >       template<class Ref, class Val, class Allocator>
   > >       class generator<Ref, Val, Allocator>::promise_type {
   > >       public:
   > >         […]
   > >         template<ranges::input_range R, class Alloc>
   > >           requires convertible_to<ranges::range_reference_t<R>, yielded> ||
   > >                    (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>,
   > >                    const remove_reference_t<yielded>&>)
   > >             auto yield_value(ranges::elements_of<R, Alloc> r);
   > >         […]
   > >       };
   > >     }

   > […]

   >     template<ranges::input_range R, class Alloc>
   >       requires convertible_to<ranges::range_reference_t<R>, yielded> ||
   >                (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>,
   >                const remove_reference_t<yielded>&>)
   >       auto yield_value(ranges::elements_of<R, Alloc> r);

   > > -13- Effects: Equivalent to:

   > > >     auto nested = [](allocator_arg_t, Alloc, ranges::iterator_t<R> i, ranges::sentinel_t<R> s)
   > > >       -> generator<yielded, void, Alloc> {
   > > >         for (; i != s; ++i) {
   > > >           co_yield static_cast<yielded>(*i);
   > > >         }
   > > >       };
   > > >     return yield_value(ranges::elements_of(nested(
   > > >       allocator_arg, r.allocator, ranges::begin(r.range), ranges::end(r.range))));

### 4421(i). Clarify the output encoding of `to_chars` for integers

Section: 28.2.2 [charconv.to.chars] Status: New Submitter: Jan Schultke Opened: 2025-10-17 Last modified: 2026-06-15

Priority: 3

View all issues with New status.

Discussion:

The integer overload of `to_chars` is specified "manually" rather than in terms of `printf`, and it is not clearly stated what encoding the output characters have. Given the existing use of the character literal `'-'` in the Effects element, the intent is presumably to output in the ordinary literal encoding.

[2025-10-23; Reflector poll.]

Set priority to 3 after reflector poll.

Concerns with meaning of `'a'..'z'` for EBCDIC. Suggestion to use `U+0061..U+007A` like in [tab:lex.charset.basic], as alternative.

Previous resolution [SUPERSEDED]

This wording is relative to N5014.

1. Modify 28.2.2 [charconv.to.chars] as indicated:

   >     constexpr to_chars_result to_chars(char* first, char* last, integer-type value, int base = 10);

   > > -4- Preconditions: `base` has a value between 2 and 36 (inclusive).

   > > -5- Effects: The value of `value` is converted to a string of digits in the given base (with no redundant leading zeroes) in the ordinary literal encoding (5.3.1 [lex.charset]). Digits in the range 10..35 (inclusive) are represented as lowercase characters `'a'..'z'`. If `value` is less than zero, the representation starts with `'-'`.

Proposed resolution:

This wording is relative to N5046.

1. Modify 28.2.2 [charconv.to.chars] as indicated:

   >     constexpr to_chars_result to_chars(char* first, char* last, integer-type value, int base = 10);

   > > -4- Preconditions: `base` has a value between 2 and 36 (inclusive).

   > > -5- Effects: The value of `value` is converted to a string of digits in the given base (with no redundant leading zeroes) in the ordinary literal encoding (5.3.1 [lex.charset]). Digits in the range 10..35 (inclusive) are represented as lowercase characters `a..z`. If `value` is less than zero, the representation starts with `'-'`.

### 4458(i). `indirect<T>` should be convertible to `T&`

Section: 20.4.1.6 [indirect.obs] Status: New Submitter: Zhihao Yuan Opened: 2025-11-05 Last modified: 2025-11-06

Priority: Not Prioritized

View all issues with New status.

Discussion:

Addresses US 77-140

In addition to serving as a better `unique_ptr<T>` in Pimpl, `indirect<T>` can also appear as a drop-in replacement for `T` when `T` may conditionally be an incomplete type. In which case, if `indirect<T>` is convertible to `T&`, certain tasks can be done without meta-programming (e.g., returning the object of type `T` or `indirect<T>` from a function with a return type `T&`).

Proposed resolution:

This wording is relative to N5014.

1. Modify 20.4.1.2 [indirect.syn], as indicated:

   >     namespace std {
   >       template<class T, class Allocator = allocator<T>>
   >       class indirect {
   >       public:
   >         […]
   >         // 20.4.1.6 [indirect.obs], observers
   >         constexpr operator const T& () const & noexcept;
   >         constexpr operator T& () & noexcept;
   >         constexpr operator const T&& () const && noexcept;
   >         constexpr operator T&& () && noexcept;
   >         constexpr const T& operator*() const & noexcept;
   >         constexpr T& operator*() & noexcept;
   >         constexpr const T&& operator*() const && noexcept;
   >         constexpr T&& operator*() && noexcept;
   >         constexpr const_pointer operator->() const noexcept;
   >         constexpr pointer operator->() noexcept;
   >         […]

2. Modify 20.4.1.6 [indirect.obs], as indicated:

   >     constexpr operator const T& () const & noexcept;
   >     constexpr operator T& () & noexcept;
   >     constexpr const T& operator*() const & noexcept;
   >     constexpr T& operator*() & noexcept;

   > > -1- Preconditions: `*this` is not valueless.

   > > -2- Returns: `*p`.

   >     constexpr operator const T&& () const && noexcept;
   >     constexpr operator T&& () && noexcept;
   >     constexpr const T&& operator*() const && noexcept;
   >     constexpr T&& operator*() && noexcept;

   > > -3- Preconditions: `*this` is not valueless.

   > > -4- Returns: `std::move(*p)`.

### 4473(i). `<cfloat>` should be specified not to contain all macros in `<float.h>`

Section: 17.3.7 [cfloat.syn] Status: New Submitter: Jan Schultke Opened: 2025-11-13 Last modified: 2026-01-16

Priority: 3

View all issues with New status.

Discussion:

Consider 17.3.6 [climits.syn] paragraph 1:

> The header `<climits>` defines all macros the same as the C standard library header `<limits.h>`, except that it does not define the macro `BITINT_MAXWIDTH`.

Similarly, `<cfloat>` is missing various macros from C's `<float.h>`, but this is not stated.

[2026-01-16; Reflector poll.]

Set priority to 3 after reflector poll.

"Could be just 'The header `<cfloat>` defines all macros shown above the same as the C standard library header `<float.h>`."

"That would resolve my concern to require C++ implementations to actively `#undef` things from `<float.h>`. It should be up to the implementation."

Previous resolution [SUPERSEDED]

This wording is relative to N5014.

1. Modify 17.3.7 [cfloat.syn] as indicated:

   > -1- The header `<cfloat>` defines all macros the same as the C standard library header `<float.h>`, except that it does not define any macros specifying characteristics of C decimal floating types or C interchange floating types.

[2026-01-16; Jonathan provides improved wording]

Proposed resolution:

This wording is relative to N5032.

1. Modify 17.3.7 [cfloat.syn] as indicated:

   > -1- The header `<cfloat>` defines all macros shown above the same as the C standard library header `<float.h>`.

### 4475(i). The description to single total order in [thread.mutex.requirements.mutex.general] is hollow

Section: 32.6.4.2.1 [thread.mutex.requirements.mutex.general] Status: Tentatively NAD Submitter: jim x Opened: 2025-11-14 Last modified: 2026-01-16

Priority: Not Prioritized

View other active issues in [thread.mutex.requirements.mutex.general].

View all other issues in [thread.mutex.requirements.mutex.general].

View all issues with Tentatively NAD status.

Discussion:

32.6.4.2.1 [thread.mutex.requirements.mutex.general] p4 says:

> For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order.

Even for atomic operations, the precondition for ordering them in a single total order is that they must be `memory_order::seq_cst` operations, such that we can form the total order to reason.

Put aside the fact that we impose the preconditions on `unlock` and `lock`. Is this a possible total order if `lock` reads `unlock_1`, but there is a `unlock_2` between them

> `unlock_1` < `unlock_2` < `lock`

First, although we have said that lock and unlock operations behave as atomic operations, and `lock` reads `unlock_1`, meaning that `unlock_1` is coherence-ordered before `lock`, however, we don't specify that they are `memory_order::seq_cst` operations, so 32.5.4 [atomics.order] p4 doesn't apply here

> Second, for every pair of atomic operations `A` and `B` on an object `M`, where `A` is coherence-ordered before `B`, the following four conditions are required to be satisfied by `S`:

> - (4.1) — if `A` and `B` are both `memory_order::seq_cst` operations, then `A` precedes `B` in `S`; and

So, it is not helpful to decide that `unlock_1` precedes `lock` in a single total order. Similarly, excluding `unlock_1` < `unlock_2` < `lock` is not possible.

Suggested resolution:

The lock and unlock operations on a single mutex appears to occur in a single total order; for this purpose, these operations are considered as `memory_order::seq_cst` operations

[2026-01-16; Reflector poll. Status → Tentatively NAD.]

For atomic objects, the modification order is already a single total order, `seq_cst` or not. This isn't a useful change.

Proposed resolution:

This wording is relative to N5014.

1. Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:

   > -4- The implementation provides lock and unlock operations, as described below. For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order; for this purpose, these operations are considered as `memory_order::seq_cst` operations.

### 4479(i). Missing restrictions on which types can be `tuple` elements

Section: 22.4.4.1 [tuple.tuple.general] Status: New Submitter: Davis Herring Opened: 2025-11-21 Last modified: 2026-01-16

Priority: 3

View all issues with New status.

Discussion:

We do not say that instantiating types like `std::tuple<void>` or `std::tuple<int()>` won't work.

[2025-11-26; Jonathan comments.]

16.4.5.8 [res.on.functions] already makes it undefined behaviour to instantiate `tuple` with an incomplete type, which includes `void` and `int[]`. It would be better if it was ill-formed not undefined though, and that doesn't say anything about `tuple<int()>` or `tuple<AbstractClassType>`. Any restrictions should only apply when `tuple` is instantiated, not just when it's declared, as that would prevent using it as a typelist.

We might need to specify that `tuple_size` and `tuple_element` can be instantiated with incomplete types that are specializations of `tuple`, because otherwise 16.4.5.8 [res.on.functions] makes that undefined. `tuple_size` and `tuple_element` don't need a complete `tuple` type to give an answer, and they don't need to instantiate a `tuple` type. So `tuple_element_t<0, tuple<void>>` should work.

[2026-01-16; Reflector poll.]

Set priority to 3 after reflector poll.

Proposed resolution:

### 4488(i). Undesired status for some functions in `<cmath>`

Section: 16.4.2.3 [headers] Status: New Submitter: Jiang An Opened: 2025-12-08 Last modified: 2026-01-16

Priority: 3

View all other issues in [headers].

View all issues with New status.

Discussion:

Originally reported in cplusplus/draft#8579.

Currently, 16.4.2.3 [headers] p5 specially mentions "except for names which are defined as macros in C", which covers `std::fpclassify` and its friends. Perhaps we don't want to require them to be initially declared in namespace `std`. Moreover, 16.4.2.3 [headers] p6 seemingly requires them to be also defined as macros in C++, which doesn't seem intentional.

Perhaps we want to exactly specify whether a name denotes a function (overload set) or a macro in C++, while no name should denote both a function and a macro, and to allow a function name from C initially declared in the global namespace.

[2026-01-16; Reflector poll.]

Set priority to 3 after reflector poll.

"Seems consistent with what all the implementation actually do."

"Macros aren't 'declared' so saying 'declarations except macros' is confusing."

Proposed resolution:

This wording is relative to N5032.

1. Modify 16.4.2.3 [headers] as indicated:

   > […]

   > -5- Except as noted in 16 [library] through 33 [exec] and D [depr], the contents of each header `cname` is the same as that of the corresponding header `name.h` as specified in the C standard library (2 [intro.refs]). In the C++ standard library, however, the declarations (except for names which are defined as macros in C++) are within namespace scope (6.4.6 [basic.scope.namespace]) of the namespace `std`. It is unspecified whether these names (including any overloads added in 17 [support] through 33 [exec] and D [depr]) are first declared within the global namespace scope and are then injected into namespace `std` by explicit using-declarations (9.10 [namespace.udecl]).

   > -6- Names which are defined as macros in C shall be defined as macros in the C++ standard librarythe C++ standard library shall not be defined as functions, even if C grants license for implementation as functions. [Note 2: The names defined as macros in C include the following: `assert`, `offsetof`, `setjmp`, `va_arg`, `va_end`, and `va_start`. — end note]

   > -7- Names that are defined as functions in C shall be defined as functions in the C++ standard librarythe C++ standard library shall not be defined as macros.(138)

### 4489(i). `concat_view::iterator`'s `iter_swap` has tautologically `false noexcept` specification

Section: 25.7.18.3 [range.concat.iterator] Status: New Submitter: Nhat Nguyen Opened: 2025-12-10 Last modified: 2026-01-19

Priority: 3

View other active issues in [range.concat.iterator].

View all other issues in [range.concat.iterator].

View all issues with New status.

Discussion:

The `noexcept` specification of `concat_view::iterator`'s `iter_swap` has `noexcept(ranges::swap(*x, *y))`, where `x` and `y` is `concat_view::iterator`. However, `concat_view::iterator`'s `operator*` does not have a `noexcept` specification, therefore `noexcept(ranges::swap(*x, *y))` is always `false` and then the `noexcept` specification of `concat_view::iterator`'s `iter_swap` is always `noexcept(false)`.

[2026-01-16; Reflector poll.]

Set priority to 3 after reflector poll.

"Might want to add `noexcept` to `operator*`." "Yes, even though it has a narrow contract."

Proposed resolution:

### 4494(i). Add `consteval` to `std::meta::exception` defaulted member functions

Section: 21.4.4 [meta.reflection.exception] Status: Tentatively NAD Submitter: Marek Polacek Opened: 2025-12-16 Last modified: 2026-01-28

Priority: Not Prioritized

View all other issues in [meta.reflection.exception].

View all issues with Tentatively NAD status.

Discussion:

CWG 3115 states that every function of consteval-only type shall be an immediate function. This caused a problem for `std::meta::exception::what()` which was marked `constexpr` but not `consteval`. To be able to mark it `consteval`, we had to tweak the rules about overriding by a consteval virtual function (CWG 3117).

But 21.4.4 [meta.reflection.exception] still defines `std::meta::exception` such that it contains these defaulted special member functions:

>     exception(const exception&) = default;
>     exception(exception&&) = default;

>     exception& operator=(const exception&) = default;
>     exception& operator=(exception&&) = default;

which aren't `consteval` (and since they're not templates, they won't be promoted to `consteval` as per P2564). I propose to make the four functions `consteval`:

>     consteval exception(const exception&) = default;
>     consteval exception(exception&&) = default;

>     consteval exception& operator=(const exception&) = default;
>     consteval exception& operator=(exception&&) = default;

[2026-01-28; Reflector poll. Status → Tentatively NAD.]

The second change in CWG 3115 fixes this.

Proposed resolution:

This wording is relative to N5032.

1. Modify 21.4.4 [meta.reflection.exception] as indicated:

   >     namespace std::meta {
   >       class exception : public std::exception {
   >       private:
   >         optional<string> what_;  // exposition only
   >         u8string u8what_;        // exposition only
   >         info from_;              // exposition only
   >         source_location where_;  // exposition only
   >       public:
   >         consteval exception(u8string_view what, info from,
   >                             source_location where = source_location::current()) noexcept;

   >         consteval exception(string_view what, info from,
   >                             source_location where = source_location::current()) noexcept;

   >         consteval exception(const exception&) = default;
   >         consteval exception(exception&&) = default;

   >         consteval exception& operator=(const exception&) = default;
   >         consteval exception& operator=(exception&&) = default;

   >         constexpr const char* what() const noexcept override;
   >         consteval u8string_view u8what() const noexcept;
   >         consteval info from() const noexcept;
   >         consteval source_location where() const noexcept;
   >       };
   >     }

### 4502(i). `std::execution::when_all` Is Never Nothrow Connectable

Section: 33.9.12.12 [exec.when.all] Status: New Submitter: Robert Leahy Opened: 2026-01-11 Last modified: 2026-02-18

Priority: 2

View all other issues in [exec.when.all].

View all issues with New status.

Discussion:

The behavior of algorithms in the standard is described in terms of exposition-only machinery 33.9.2 [exec.snd.expos]. Particularly the behavior of `std::execution::connect` 33.9.10 [exec.connect] for such algorithms is described in terms of `basic-sender::connect` which is conditionally `noexcept`:

> The expression in the `noexcept` clause of the `connect` member function of `basic-sender` is:

>     is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>

The constructor of `basic-operation` is also conditionally `noexcept`:

> The expression in the `noexcept` clause of the constructor of `basic-operation` is:

>     is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
>     noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))

This cascade of chasing conditional `noexcept` doesn't end there, instead moving to `basic-state`:

> The expression in the `noexcept` clause of the constructor of `basic-state` is

>     is_nothrow_move_constructible_v<Rcvr> &&
>     nothrow-callable>decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&> &&
>     (same_as<state-type<Sndr, Rcvr>, get-state-result> ||
>      is_nothrow_constructible_v<state-type<Sndr, Rcvr>, get-state-result>)

> where `get-state-result` is

> `call-result-t<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>`.

This finally implicates something in 33.9.12.12 [exec.when.all] as we must now look to the `noexcept` clause of its `get-state` implementation:

> The member `impls-for<when_all_t>::get-state` is initialized with a callable object equivalent to the following lambda expression:

>     []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
>       return e;
>     }

> where `e` is the expression

>     std::forward<Sndr>(sndr).apply(make-state<Rcvr>())

> and where `make-state` is the following exposition-only class template:

>     enum class disposition { started, error, stopped };             // exposition only

>     template<class Rcvr>
>     struct make-state {
>       template<class... Sndrs>
>       auto operator()(auto, auto, Sndrs&&... sndrs) const {
>         using values_tuple = see below;
>         using errors_variant = see below;
>         using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;

>         struct state-type {
>           void arrive(Rcvr& rcvr) noexcept {                        // exposition only
>             if (0 == --count) {
>               complete(rcvr);
>             }
>           }

>           void complete(Rcvr& rcvr) noexcept;                       // exposition only

>           atomic<size_t> count{sizeof...(sndrs)};                   // exposition only
>           inplace_stop_source stop_src{};                           // exposition only
>           atomic<disposition> disp{disposition::started};           // exposition only
>           errors_variant errors{};                                  // exposition only
>           values_tuple values{};                                    // exposition only
>           optional<stop_callback> on_stop{nullopt};                 // exposition only
>         };

>         return state-type{};
>       }
>     };

Notice that the overloaded function call operator does not have a `noexcept` clause meaning connecting a `std::execution::when_all` sender is never `noexcept`.

This is a library wording issue because if the design intent was for the above-described operation to be unconditionally `noexcept(false)` there would be no reason for `get-state` to feature `noexcept(noexcept(e))` (since that expression would've been intended to be a contradiction).

Moreover in returning a default-initialized `state-type` the above-discussed function call operator performs the following operations:

- Initializes an `atomic<size_t>` with an initial value, which does not throw,
- Default-constructs an `inplace_stop_source`, which is `noexcept(true)` per 32.3.9.3 [stopsource.inplace.mem],
- Initializes an `atomic<disposition>` with an initial value, which does not throw,
- Initializes two variants by default constructing a `monostate` alternative, which does not throw, and
- Initializes a disengaged optioanal, which does not throw

Since none of these throw the overloaded function call operator can be made `noexcept(true)`.

[2026-02-18; Reflector poll.]

Set priority to 2 after reflector poll.

Wording needs additional tightening because `value_type` isn't a variant and `errors_variant` does not use `monostate`.

Proposed resolution:

Update 33.9.12.12 [exec.when.all] as follows:

> [...]

> and where `make-state` is the following exposition-only class template:

>     enum class disposition { started, error, stopped };             // exposition only

>     template<class Rcvr>
>     struct make-state {
>       template<class... Sndrs>
>       auto operator()(auto, auto, Sndrs&&... sndrs) const noexcept {
>         using values_tuple = see below;
>         using errors_variant = see below;
>         using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;

>         struct state-type {
>           void arrive(Rcvr& rcvr) noexcept {                        // exposition only
>             if (0 == --count) {
>               complete(rcvr);
>             }
>           }

>           void complete(Rcvr& rcvr) noexcept;                       // exposition only

>           atomic<size_t> count{sizeof...(sndrs)};                   // exposition only
>           inplace_stop_source stop_src{};                           // exposition only
>           atomic<disposition> disp{disposition::started};           // exposition only
>           errors_variant errors{};                                  // exposition only
>           values_tuple values{};                                    // exposition only
>           optional<stop_callback> on_stop{nullopt};                 // exposition only
>         };

>         return state-type{};
>       }
>     };

> [...]

### 4503(i). Use of `std::common_reference_with` in `std::assignable_from` seems incorrect

Section: 18.4.8 [concept.assignable] Status: LEWG Submitter: Jiang An Opened: 2025-12-24 Last modified: 2026-02-18

Priority: 3

View all issues with LEWG status.

Discussion:

Currently, `std::assignable_from<std::unique_ptr<int>&, std::nullptr_t>` is `false`, because the common reference type of `unique_ptr<int>&` and `nullptr_t` is `unique_ptr<int>`, and a const `unique_ptr<int>` lvalue can't be converted to the common reference type.

Such design seems counter-intuitive and valueless. When `common_reference_with` is modeled, one can use the common reference type in some non-generic, homogeneous interfaces. However, it should be clear enough that the type isn't expected to be used in assignment.

The earliest form of the design can be found in ericniebler/stl2#150, where cases involving move-only types didn't seem analyzed. In any case, if we want `ranges::advance` to perform assignment only when `I` and `S` are common enough, we should just limit the `common_reference_with` requirements to `ranges::advance`.

[2026-02-18; Reflector poll. Status → LEWG.]

This is clearly a design question that needs a paper.

Proposed resolution:

This wording is relative to N5032.

1. Modify 18.4.8 [concept.assignable] as indicated:

   >     template<class LHS, class RHS>
   >       concept assignable_from =
   >         is_lvalue_reference_v<LHS> &&
   >         common_reference_with<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&> &&
   >         requires(LHS lhs, RHS&& rhs) {
   >           { lhs = std::forward<RHS>(rhs) } -> same_as<LHS>;
   >         };

### 4505(i). The `std::make_optional` overload taking `std::initializer_list` should not accept a reference type as its template argument

Section: 22.5.10 [optional.specalg] Status: New Submitter: Jiang An Opened: 2025-12-25 Last modified: 2026-02-18

Priority: 4

View all other issues in [optional.specalg].

View all issues with New status.

Discussion:

`std::make_optional<std::initializer_list<int>&>({1, 2})` is currently valid, but the returned `optional` object contains a dangling reference, because the stored reference used to refer to the `initializer_list<int>` parameter object of that `make_optional` overload.

Moreover, when there's already an `initializer_list<T>` variable `il`, `std::make_optional<std::initializer_list<T>&>(il)` also returns an `optional` containing a dangling reference, because the third `make_optional` overload in 22.5.10 [optional.specalg] is more specialized than the second and thus gets selected.

Given `optional<T&>` has no specific initializer-list constructor, perhaps we should make the `make_optional` overload taking `initializer_list` not accept `T&` as its first template argument.

[2026-02-18; Reflector poll.]

Set priority to 4 after reflector poll.

This only affects edge cases like `make_optional<initializer_list<int>&>({....})`. The `is_constructible_v` constraint seems unmotivated by the issue.

Proposed resolution:

This wording is relative to N5032.

1. Modify 22.5.10 [optional.specalg] as indicated:

   > [Drafting note: This also make the implicit SFINAE constraints introduced by 16.3.2.4 [structure.specifications] p4 explicit.]

   >     template<class T, class U, class... Args>
   >       constexpr optional<T> make_optional(initializer_list<U> il, Args&&... args);

   > > -?- Constraints: `is_lvalue_reference_v<T>` is `false` and `is_constructible_v<T, initializer_list<U>&, Args...>` is `true`.

   > > -6- Effects: Equivalent to: `return optional<T>(in_place, il, std::forward<Args>(args)...);`

### 4507(i). The order of side effects initiated by I/O functions is not described by happen-before

Section: 31 [input.output] Status: Core Submitter: jim x Opened: 2025-12-29 Last modified: 2026-02-18

Priority: 4

View other active issues in [input.output].

View all other issues in [input.output].

View all issues with Core status.

Discussion:

6.10.1 [intro.execution] p7 says (emphasis mine)

> Reading an object designated by a `volatile` glvalue (7.2.1 [basic.lval]), modifying an object, producing an injected declaration (7.7 [expr.const]), calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution or translation environment. Evaluation of an expression (or a subexpression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access through a `volatile` glvalue is evaluated, the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the `volatile` access may not have completed yet.

Consider this example:

>     #include <thread>
>     #include <atomic>
>     #include <stream>

>     int main(){
>       std::ofstream outFile("example.txt", std::ios::out | std::ios::trunc);
>       std::atomic<bool> flag = false;
>       auto t1 = std::thread([&](){
>         outFile << "ab"; // #1
>         flag.store(true, std::memory_order::release); // #2
>       });
>       auto t2 = std::thread([&](){
>         while (!flag.load(std::memory_order::acquire)); // #3
>         outFile << "cd"; // #4
>       });
>       t1.join();
>       t2.join();
>     }

Because `#2` synchronizes with `#3`, then `#1` happens before `#4`. According to 4.1.2 [intro.abstract] p8

> The following specify the observable behavior of the program:

> - (8.1) — […]

> - (8.2) — Data is delivered to the host environment to be written into files (See also: ISO/IEC 9899:2024, 7.23.3).

> - (8.3) — […]

So, writing data to files is observable behavior. Are data guaranteed to be delivered to the file `example.txt` in the order "abcd"? I don't find a relevant rule in both subclauses 6.10.2.2 [intro.races] and 31 [input.output] that specifies the order of delivery of data to files depends on the happen-before relationship.

6.10.2.2 [intro.races] only states how memory visibility is defined according to happen-before. Both reading an object designated by a `volatile` glvalue and modifying an object can be categories to side effects on memory locations, 6.10.2.2 [intro.races] does define that. However, the side effects produced by the call to a library I/O function might not be about memory visibility. 31 [input.output] only mentions when a data race happens. 6.10.1 [intro.execution] p7 only says that when the calling of a function at `#1` returns, the side effect is considered complete. However, it lacks how the completion is related to the expression that is sequenced after it, let alone how the completion is ordered relative to `#3` that inter-threaded happens after it.

In the whole standard, there is only one informal note specifying this at the end of 6.10.2.2 [intro.races] p8:

> [Note 8: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. — end note]

However, this is an informal note, and I didn't find a formal rule corresponding to this note.

Suggested Resolution:

The English meaning of happen before means one thing has done before the other. However, happen-before as the term of the C++ standard, we didn't give it that meaning. IIUC, the intended meaning should be that: if A happens before B, then A will finish before B starts. This meaning is not embodied in the formal rules. We only describe memory visibility in terms of happen-before, but don't for other kinds of side effects or observable behaviors.

We should define how the order of side effects produced by I/O functions is defined by happen-before.

[2026-02-18; Reflector poll.]

Set status to Core and priority to 4 after reflector poll.

Low participation.

Proposed resolution:

### 4508(i). Meaning of "compatible" and C++ desire for C23 changes unclear for `va_arg`

Section: 17.14.2 [cstdarg.syn] Status: New Submitter: Hubert Tong Opened: 2025-12-15 Last modified: 2026-02-18

Priority: 2

View other active issues in [cstdarg.syn].

View all other issues in [cstdarg.syn].

View all issues with New status.

Discussion:

The wording in subclause 7.16.1.1 of both C17 and C23 has

> "[if] type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions) [...]".

17.14.2 [cstdarg.syn] only clarifies that the default argument promotions are replaced with the C++ version, seemingly leaving the C definition of "compatible" as operative for C++ in this context.

Consider, for example, the topic of CWG 3046, "Enumerations as part of the common initial sequence": Enumerations with the same underlying type need not have equivalent sets of values in C++, whereas C enumerations with the same underlying type are compatible and capable of representing the same values. Thus, enumerations should only be defined by C++ as being layout-compatible if, in addition to having the same underlying type, they also have equivalent sets of values. This difference between C++'s "layout-compatible" types and C's "compatible" types means that we should avoid using C's "compatible" in C++ contexts.

Furthermore, C23 expanded the bullets.

It now reads:

> […] except for the following cases:

> - both types are pointers to qualified or unqualified versions of compatible types;

> - one type is compatible with a signed integer type, the other type is compatible with the corresponding unsigned integer type, and the value is representable in both types;

> - one type is pointer to qualified or unqualified void and the other is a pointer to a qualified or unqualified character type;

> - or, the type of the next argument is nullptr_t and type is a pointer type that has the same representation and alignment requirements as a pointer to a character type.

Assuming that "compatible" is read as "layout-compatible", the first bullet, whose effect in C is to allow limited differences in qualification, allows further differences in the pointee types in C++ because pointers are not layout-compatible in C++ when the pointee types are layout-compatible but not the same type.

Clearer wording, likely with more restrictions than C, is needed. A question, though, is whether "compatible" should generally be replaced (for `va_arg`) with "layout-compatible" or a requirement for being the same type. In the specific case of the first bullet, I believe the latter is the right choice.

[2026-02-18; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

### 4509(i). `std::optional<T>::transform` cannot be implemented while supporting program-defined specializations

Section: 22.5.3.8 [optional.monadic] Status: LEWG Submitter: Rasheeq Azad Opened: 2025-12-24 Last modified: 2026-02-18

Priority: 3

View all other issues in [optional.monadic].

View all issues with LEWG status.

Discussion:

Currently (that is, as of the draft at N5032), 22.5.3.8 [optional.monadic] specifies that `std::optional<T>::transform(F&&f)&` shall do the following (and similar for the other overloads):

> Let `U` be `remove_cv_t<invoke_result_t<F, decltype((val))>>`.

> Mandates: […]

> [Note 1: There is no requirement that `U` is movable (9.5.1 [dcl.init.general]). — end note]

> Returns: If `*this` contains a value, an `optional<U>` object whose contained value is direct-non-list-initialized with `invoke(std::forward<F>(f), val)`; otherwise, `optional<U>()`.

However, none of the standard constructors or other member functions of `optional<U>` provide a surefire way to initialize the contained `U` value with an expression like `invoke(std::forward<F>(f), val)`. The closest are the `in_place_t`/`emplace` overloads, which almost but not quite admit a generic implementation of `transform`. This looks roughly like:

>     namespace std {
>       template<class _F> struct __later {
>         _F __f;
>         operator decltype(std::move(__f)())() && { return std::move(__f)(); }
>       };

>       template<class _T> class optional {
>         // etc.
>       public:
>         template<class _F> constexpr auto transform(_F &&__f) & {
>           using _U = remove_cv_t<invoke_result_t<_F, _T&>>;
>           if(!has_value()) return optional<_U>();
>           return optional<_U>(in_place, __later([&] -> _U {
>             return std::invoke(std::forward<_F>(__f), value());
>           }));
>         }
>       };
>     }

Unfortunately, this does not quite meet the specification. The issue is if `U` is a type with a `U(auto&&)` constructor:

>     struct oops {
>       oops() = default;
>       oops(auto&&) { std::cout << "launching missiles\n"; }
>     };

>     int main() {
>       std::optional<int> oi(5);
>       oi.transform([](auto& i) { return oops(); });
>       // missiles get launched when they shouldn't
>     }

In this case, the rules for direct-initialization (see 9.5 [dcl.init] bullet 16.6.2) will select the template constructor over the conversion function on the `__later` specialization. [Complete example 1]

To avoid this problem, standard library implementors generally implement `std::optional<T>::transform` with a non-standard constructor on their `std::optional<T>` primary template; roughly:

>     namespace std {
>       struct __optional_from_invocable_tag {
>         constexpr explicit __optional_from_invocable_tag() { }
>       };

>       template<typename _T>
>       class optional {
>         // etc.
>       public:
>         template<typename _F, typename _V>
>         constexpr optional(__optional_from_invocable_tag, _F &&__f, _V &&__v)
>           : __present(true)
>           , __val(std::invoke(std::forward<_F>(__f), std::forward<_V>(__v)))
>         { }

>         template<class _F> constexpr auto transform(_F &&__f) & {
>           using _U = remove_cv_t<invoke_result_t<_F, _T&>>;
>           if(!has_value()) return optional<_U>();
>           return optional<_U>(
>             __optional_from_invocable_tag(),
>             std::forward<_F>(__f), value());
>         }
>       };
>     }

[Complete example 2]. Note that the missiles are not launched.

Now for the real issue: if a user program wants to specialize `std::optional` for a program-defined type, it will have to explicitly rely on these details of its standard library implementation in order to be supported by the standard library's `transform` implementation. Specifically, it will have to provide a non-standard constructor with a signature matching the library implementation's expectations. (A portable implementation of `transform` itself is more-or-less possible for a program-defined specialization by using a circumlocution like `std::optional<std::monostate>(std::in_place).transform(/* ... */)`.)

The root problem is that the standard interface of `std::optional<U>` provides for direct-initialization of the contained `U` by arbitrary glvalues, but not by an arbitrary prvalue (that is, by calling an arbitrary invocable). This forces library implementations to invent their own non-standard interfaces for doing so, which then makes it impossible for those implementations to support program-defined specializations of `std::optional` that only meet the minimal requirements of the standard, and do not support those non-standard interfaces.

The fact that `std::optional<T>::transform` makes implementing `std::optional` while supporting program-defined specializations basically impossible does not appear to be intentional. P0798R8, which introduced `std::optional<T>::transform`, does not mention this side-effect of its standardization.

There are at least two different resolutions that immediately come to mind.

Option A: Forbid program-defined `std::optional<T>` specializations

Taking this option would immediately solve the problem. However, in my opinion, this would be unnecessarily restrictive. Specializing `std::optional` is a useful thing to allow, as it allows replacing the common `struct optional<T> { union { T val; }; bool present; }` representation with something more compact when `T` has unused values/unused bits.

Option B: Add a `std::optional<T>` constructor taking an invocable

This option more-or-less formalizes existing practice, using a type tag to gate the new constructor. It would be ideal to extend this idea to `emplace` and then to the various `in_place_t` constructors and `emplace` functions in other parts of the standard, but the wording presented here is restricted to fixing this issue.

Changing `std::optional<T&>` doesn't seem strictly necessary, but introducing a nonuniformity seems like a bad idea. I'm not 100% certain about the wording for the new constructors.

[2026-02-18; Reflector poll.]

Set status to LEWG and priority to 3 after reflector poll.

Either option needs LEWG confirmation. Option A reflects existing practice.

Proposed resolution:

This wording is relative to N5032.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A: Forbid program-defined `std::optional<T>` specializations

1. Modify 22.5.3.1 [optional.optional.general] as indicated:

   > -2- A type `X` is a valid contained type for `optional` […]. If `T` is an object type, `T` shall meet the Cpp17Destructible requirements (Table 35).

   > -?- The behavior of a program that adds a specialization for `optional` is undefined.

Option B: Add a `std::optional<T>` constructor taking an invocable

1. Modify 22.2.1 [utility.syn], header `<utility>` synopsis, as indicated:

   >     […]
   >     namespace std {
   >       […]
   >       template<size_t I>
   >         struct in_place_index_t {
   >           explicit in_place_index_t() = default;
   >         };
   >       template<size_t I> constexpr in_place_index_t<I> in_place_index{};

   >       // construction from arbitrary initializers

   >       struct from_continuation_t {
   >         explicit from_continuation_t() = default;
   >       };
   >       inline constexpr from_continuation_t from_continuation{};
   >       […]
   >     }

2. Modify 22.5.3.1 [optional.optional.general] as indicated:

   >     namespace std {
   >       template<class T>
   >       class optional {
   >       public:
   >         […]
   >         // 22.5.3.2 [optional.ctor], constructors
   >         constexpr optional() noexcept;
   >         constexpr optional(nullopt_t) noexcept;
   >         […]
   >         template<class... Args>
   >           constexpr explicit optional(in_place_t, Args&&...);
   >         template<class U, class... Args>
   >           constexpr explicit optional(in_place_t, initializer_list<U>, Args&&...);
   >         template<class F, class... Args>
   >           constexpr explicit optional(from_continuation_t, F&&, Args&&...);
   >         template<class U = remove_cv_t<T>>
   >           constexpr explicit(see below) optional(U&&);
   >         […]
   >       };
   >       […]
   >     }

3. Modify 22.5.3.2 [optional.ctor] as indicated:

   >     template<class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > -18- Constraints: […]

   > > […]

   > > -22- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class F, class... Args>
   >       constexpr explicit optional(from_continuation_t, F&& f, Args&&... args);

   > > -?- Mandates: `decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...))` is `T`.

   > > -?- Effects: Direct-non-list-initializes `val` with `std::invoke(std::forward<F>(f), std::forward<Args>(args)...)`.

   > > -?- Postconditions: `*this` contains a value.

4. Modify 22.5.4.1 [optional.optional.ref.general] as indicated:

   >     namespace std {
   >       template<class T>
   >       class optional<T&> {
   >       public:
   >         […]
   >         // 22.5.4.2 [optional.ref.ctor], constructors
   >         constexpr optional() noexcept = default;
   >         constexpr optional(nullopt_t) noexcept : optional() {}
   >         […]
   >         template<class Arg>
   >           constexpr explicit optional(in_place_t, Arg&& arg);
   >         template<class F, class... Args>
   >           constexpr explicit optional(from_continuation_t, F&& f, Args&&... args);
   >         template<class U>
   >           constexpr explicit(see below) optional(U&& u) noexcept(see below);
   >         […]
   >       };
   >       […]
   >     }

5. Modify 22.5.4.2 [optional.ref.ctor] as indicated:

   >     template<class U, class Arg>
   >       constexpr explicit optional(in_place_t, Arg&& arg);

   > > -1- Constraints: […]

   > > -2- Effects: […]

   > > -3- Postconditions: […]

   >     template<class F, class Arg>
   >       constexpr explicit optional(from_continuation_t, F&& f, Arg&& arg);

   > > -?- Mandates: `decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...))` is `T&`.

   > > -?- Effects: Equivalent to: `convert-ref-init-val(std::invoke(std::forward<F>(f), std::forward<Args>(args)...))`.

   > > -?- Postconditions: `*this` contains a value.

### 4516(i). `ranges` namespace is used inconsistently in [algorithms]

Section: 26 [algorithms] Status: New Submitter: Ruslan Arutyunyan Opened: 2025-12-12 Last modified: 2026-02-18

Priority: 4

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with New status.

Discussion:

`ranges` namespace is used inconsistently in 26 [algorithms]. One example is:

>     template<random_access_range R, class Comp = ranges::less, class Proj = identity>
>       requires sortable<iterator_t<R>, Comp, Proj>
>       constexpr borrowed_iterator_t<R>
>         ranges::push_heap(R&& r, Comp comp = {}, Proj proj = {});

where we can see `ranges::` before `push_heap` name itself, also before `less` but not before `random_access_range`. It's all over the place. The same is with `sort`:

>     template<random_access_range R, class Comp = ranges::less, class Proj = identity>
>       requires sortable<iterator_t<R>, Comp, Proj>
>       constexpr borrowed_iterator_t<R>
>         ranges::sort(R&& r, Comp comp = {}, Proj proj = {});

Another example is `for_each` where we don't have `ranges` namespace before `borrowed_iterator_t`:

>     template<input_range R, class Proj = identity,
>              indirectly_unary_invocable<projected<iterator_t<R>, Proj>> Fun>
>       constexpr ranges::for_each_result<borrowed_iterator_t<R>, Fun>
>         ranges::for_each(R&& r, Fun f, Proj proj = {});

There is no clear resolution for that, but during an LWG session is was decided to create an LWG issue for this.

[2026-02-18; Reflector poll.]

Set priority to 4 after reflector poll.

Recollection of the intended rule was to use `std::` and `ranges::` if the same name is in both namespaces (like `less`, etc.). This is the result of an editorial decision.

Proposed resolution:

### 4519(i). No formal rule associates the total order of a mutex object with the rule for modification order

Section: 32.6.4.2.1 [thread.mutex.requirements.mutex.general] Status: SG1 Submitter: jim x Opened: 2026-02-05 Last modified: 2026-02-27

Priority: 4

View other active issues in [thread.mutex.requirements.mutex.general].

View all other issues in [thread.mutex.requirements.mutex.general].

View all issues with SG1 status.

Discussion:

32.6.4.2.1 [thread.mutex.requirements.mutex.general] p4 says (emphasis mine):

> For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order.

The second note in 32.6.4.2.1 [thread.mutex.requirements.mutex.general] p4

> [Note 2: This can be viewed as the modification order of the mutex. — end note]

wants to inform the reader to interpret the single total order as a modification order. The review in LWG 4475(i) says:

> For atomic objects, the modification order is already a single total order, `seq_cst` or not. This isn't a useful change.

This implies that we want the single total order of a mutex object to be considered as the modification order of an atomic object for defining the order of the single total order by existing rules (especially, 6.10.2.2 [intro.races] p11-p14).

However, the wording in 32.6.4.2.1 [thread.mutex.requirements.mutex.general] p1 (emphasis mine)

> In this description, `m` denotes an object of a mutex type.

strongly implies that there is a difference between atomic objects and mutex objects. Except for the note, there is no formal wording to state that `lock()` and `unlock()` operations are modifications to the mutex object as if they were modifications to an atomic object for the purpose of determining the total order, that is, the rules defined in 6.10.2.2 [intro.races] that applies to an atomic object can also apply to an mutex object.

For example:

>     #include <mutex>

>     std::mutex m;

>     int main(){
>       m.lock(); // #1
>       m.unlock(); // #2
>     }

The current formal wording only says `m` has a single total order. There is no formal wording that would associate the order of these operations in the total order of `m` with happens-before. We do want write-write coherence to apply to the total order of a mutex object. However, 6.10.2.2 [intro.races] p11 (emphasis mine),

> If an operation `A` that modifies an atomic object `M` happens before an operation `B` that modifies `M`, then `A` is earlier than `B` in the modification order of `M`.

only applies to an atomic object, as shown in the emphasized text. The definition of total order only says the order can be either `#1 < #2` or `#2 < #1`, either one of which is valid. Presumably, the intended meaning should be that the total order of the mutex object `m` in this program is `#1 < #2` according to happens-before and 6.10.2.2 [intro.races] p11.

[2026-02-27; Reflector poll.]

Set priority to 4 after reflector poll.

Opposition to the current proposed resolution was expressed, as neither improving correctness nor clarity.

Proposed resolution:

This wording is relative to N5032.

1. Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:

   > -4- The implementation provides lock and unlock operations, as described below. For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). Therefore, the lock and unlock operations on a single mutex appears to occur in a single total order as if they were modifications to a hypothetical atomic object corresponding to the mutex object.

   > [Note 2: This can be viewed as the modification order (6.10.2 [intro.multithread]) of the mutex. Specifically, the requirements in 6.10.2.2 [intro.races] that are imposed on the modification order of an atomic object are also imposed on the total order of the mutex. — end note]

### 4524(i). Wrong deduced return type in [exec.schedule.from]

Section: 33.9.12.7 [exec.schedule.from] Status: New Submitter: Brian Bi Opened: 2026-01-20 Last modified: 2026-02-27

Priority: 2

View all other issues in [exec.schedule.from].

View all issues with New status.

Discussion:

I think there's a problem in the definition of `receiver-type` in 33.9.12.7 [exec.schedule.from] p10:

When `state-type` is instantiated, `operation_t` must be determined. To determine `operation_t`, we need to know the environment type for `receiver_t`, which requires instantiating the definition of `receiver_t::get_env` because of the deduced return type. That definition requires `state-type` to be already complete because it uses `state->rcvr`, so we have a circular dependency.

The return type should probably be written out explicitly as `FWD-ENV-T(env_of_t<Rcvr>)` or something like that (except that it seems like `Rcvr` is already "out of scope" here, so I don't know how to word it exactly).

[2026-02-27; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

### 4530(i). Unclear specification for `formatter<charT[N], charT>`

Section: 28.5.6.4 [format.formatter.spec] Status: LEWG Submitter: Ivan Lazaric Opened: 2026-02-24 Last modified: 2026-05-22

Priority: 2

View other active issues in [format.formatter.spec].

View all other issues in [format.formatter.spec].

View all issues with LEWG status.

Discussion:

According to 28.5.6.4 [format.formatter.spec] p2 bullet (2.2), the C++ spec specifies `formatter<charT[N], charT>` specialization as enabled, but it doesn’t specify how it formats the array.

Currently there is implementation divergence on this:

- libstdc++ formats all characters of the array,

- while libc++ and MSVC format up to the first null character.

This can be observed by the following code example (See also godbolt demo, last one shows the outcome for fmt library):

>     #include <print>

>     template<typename T>
>     struct wrapper {
>       const T& ref;
>     };

>     template<typename T>
>     struct std::formatter<wrapper<T>, char> {
>       std::formatter<T, char> underlying;
>       constexpr auto parse(auto& ctx) { return underlying.parse(ctx); }
>       auto format(wrapper<T> w, auto& ctx) const { return underlying.format(w.ref, ctx); }
>     };

>     int main() {
>       std::print("{:?}\n", "hello\nworld");
>       std::print("{:?}\n", wrapper("hello\nworld"));
>     }

Clarifying libstdc++ behaviour:

First print statement decays the array into a pointer in basic_format_arg construction:

gcc/libstdc-v3/include/std/format#L4535-L4545

gcc/libstdc-v3/include/std/format#L4440-L4443

So it gets formatted through `formatter<const char*, char>`. The second print statement goes through `formatter<char[12], char>`, which emits the null character at the end as well.

In essence, libstdc++ `formatter<char[N], char>` formats it as `string_view(arr, N)`, while others format as `string_view(arr)`.

[2026-03-25; Reflector poll.]

Set priority to 2 after reflector poll.

[2026-05-22 LWG telecon; Status changed: New → LEWG]

Proposed resolution:

### 4538(i). `member_offset::total_bits` should be `noexcept`

Section: 21.4.12 [meta.reflection.layout] Status: New Submitter: Hewill Kang Opened: 2026-03-10 Last modified: 2026-04-13

Priority: 3

View other active issues in [meta.reflection.layout].

View all other issues in [meta.reflection.layout].

View all issues with New status.

Discussion:

This member simply returns the result of integer calculations, which can be `noexcept`.

[2026-04-13; Reflector poll.]

Set priority to 3 after reflector poll.

"The calculation can overflow `ptrdiff_t` which would be undefined, so there's an implicit narrow contract."

Proposed resolution:

This wording is relative to N5032.

1. Modify 21.4.12 [meta.reflection.layout] as indicated:

   >     struct member_offset {
   >       ptrdiff_t bytes;
   >       ptrdiff_t bits;
   >       constexpr ptrdiff_t total_bits() const noexcept;
   >       auto operator<=>(const member_offset&) const = default;
   >     };

   >     constexpr ptrdiff_t member_offset::total_bits() const noexcept;

   > > -1- Returns: `bytes * CHAR_BIT + bits`.

### 4539(i). Undefined behavior in `std::bit_cast` should be diagnosed during constant evaluation

Section: 22.11.3 [bit.cast] Status: New Submitter: Jan Schultke Opened: 2026-03-10 Last modified: 2026-04-13

Priority: 3

View all issues with New status.

Discussion:

Consider the following example with undefined behavior, which assumes that `sizeof(int)` equals `4`:

>     struct alignas(4) E { };
>     constexpr auto x = std::bit_cast<int>(E{});

The example compiles with MSVC without any error or warning, where `x` has the value `0`, which is valid if the UB is library UB; other compilers reject it.

22.11.3 [bit.cast] paragraph 4, bullet 2 explicitly states:

> Otherwise, if `b` is indeterminate, the behavior is undefined.

The fact that undefined behavior is called out explicitly within library wording suggests that this is "library UB", not "core UB" that would prevent the initializer from being a constant expression because it violates the requirement of 7.7 [expr.const] paragraph 21 bullet 2.3:

> no constituent value of scalar type is an indeterminate or erroneous value (6.8.5 [basic.indet]),

Similarly, 22.11.3 [bit.cast] paragraph 4 states that forming a value representation that is not valid for the type is undefined, which may also be seen as "library UB". This can occur in `bit_cast<bool>(char{2})` if `2` is not a valid representation of `bool`.

During the 2026-03-10 LEWG telecon discussing P3969R0, LEWG polled whether this UB should be diagnosed.

> POLL: The undefined behavior in `bit_cast` should be diagnosable during constexpr.
> Attendance: 19
> Authors' opinion: N/A
> Outcome: No objection to unanimous consent.

[2026-04-13; Reflector poll.]

Set priority to 3 after reflector poll.

Concerns about requiring the compiler to understand the semantics of program-defined types.

Proposed resolution:

This wording is relative to N5032.

1. Modify 22.11.3 [bit.cast] as indicated:

   >     template<class To, class From>
   >       constexpr To bit_cast(const From& from) noexcept;

   > > -1- Constraints: […]

   > > -2- Mandates: Neither `To` nor `From` are consteval-only types (6.9.1 [basic.types.general]).

   > > -3- Constant When: `To`, `From`, and the types of all subobjects of `To` and `From` are types `T` such that: […]

   > > -4- Returns: An object of type `To`. Implicitly creates objects nested within the result (6.8.2 [intro.object]). Each bit of the value representation of the result is equal to the corresponding bit in the object representation of from. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object's type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified. A bit in the value representation of the result is indeterminate if it does not correspond to a bit in the value representation of from or corresponds to a bit for which the smallest enclosing object is not within its lifetime or has an indeterminate value (6.8.5 [basic.indet]). A bit in the value representation of the result is erroneous if it corresponds to a bit for which the smallest enclosing object has an erroneous value. For each bit `b` in the value representation of the result that is indeterminate or erroneous, let `u` be the smallest object containing that bit enclosing `b`:

   > > […]

   > > The result does not otherwise contain any indeterminate or erroneous values.

   > > -?- Remarks: A function call expression that has undefined or erroneous behavior as described above is not a core constant expression.

### 4541(i). Unclear behavior for parsing locale-dependent format

Section: 28.3.4.6.2.3 [locale.time.get.virtuals], 30.13 [time.parse] Status: SG16 Submitter: S. B. Tam Opened: 2026-03-14 Last modified: 2026-04-13

Priority: 3

View other active issues in [locale.time.get.virtuals].

View all other issues in [locale.time.get.virtuals].

View all issues with SG16 status.

Discussion:

Both `time_get::do_get` and `chrono::from_stream` can take format specifiers that depend on locale-specific information.

For example, the `%c` specifier corresponds to "the locale's date and time representation." It is unclear how the locale's date and time format is determined for the purpose of parsing, and this format may vary among implementations. The format may also depend on the operating system, the C runtime, and the user's preference.

In practice, locale-specific information is rarely used for parsing. If people want to get a time point from a string, they would require the string to be in a specific locale-independent format (e.g. `%F %T`).

Nonetheless, since locale-dependent specifiers are permitted, I believe the standard should clarify what they mean.

The proposed wording makes them implementation-defined, which matches `time_put::do_put` (28.3.4.6.4.3 [locale.time.put.virtuals] p1) and also matches the proposed resolution of LWG 4285(i).

[2026-04-13; Reflector poll.]

Set priority to 3 after reflector poll. Set status to SG16.

"Could potentially do something more sensible for `std::locale::classic()`."

Proposed resolution:

This wording is relative to N5032.

1. Modify 28.3.4.6.2.3 [locale.time.get.virtuals] as indicated:

   >     iter_type do_get(iter_type s, iter_type end, ios_base& f,
   >                      ios_base::iostate& err, tm* t, char format, char modifier) const;

   > > -11- Preconditions: `t` points to an object.

   > > -12- Effects: The function starts by evaluating `err = ios_base::goodbit`. It then reads characters starting at `s` until it encounters an error, or until it has extracted and assigned those `tm` members, and any remaining format characters, corresponding to a conversion specification appropriate for the POSIX function `strptime`, formed by concatenating `'%'`, the modifier character, when non-NUL, and the `format` character, except that the sequence of characters extracted for those specifiers that are described as depending on the C locale are instead implementation-defined. When the concatenation fails to yield a complete valid directive the function leaves the object pointed to by `t` unchanged and evaluates `err |= ios_base::failbit`. When `s == end` evaluates to `true` after reading a character the function evaluates `err |= ios_base::eofbit`.

   > > -13- For complex conversion specifications such as `%c`, `%x`, or `%X`, or conversion specifications that involve the optional modifiers `E` or `O`, when the function is unable to unambiguously determine some or all `tm` members from the input sequence `[s, end)`, it evaluates `err |= ios_base::eofbit`. In such cases the values of those `tm` members are unspecified and may be outside their valid range.

   > > -14- Returns: An iterator pointing immediately beyond the last character recognized as possibly part of a valid input sequence for the given `format` and `modifier`.

   > > -15- Remarks: It is unspecified whether multiple calls to `do_get()` with the address of the same `tm` object will update the current contents of the object or simply overwrite its members. Portable programs should zero out the object before invoking the function.

2. Modify 30.13 [time.parse] as indicated:

   > -1- Each `parse` overload specified in this subclause calls `from_stream` unqualified, so as to enable argument-dependent lookup (6.5.4 [basic.lookup.argdep]). In the following paragraphs, let `is` denote an object of type `basic_istream<charT, traits>` and let `I` be `basic_istream<charT, traits>&`, where `charT` and `traits` are template parameters in that context.

   > -2- Recommended practice: Implementations should make it difficult to accidentally store or use a manipulator that may contain a dangling reference to a format string, for example by making the manipulators produced by parse immovable and preventing stream extraction into an lvalue of such a manipulator type.

   > […]

   > -17- If the `from_stream` overload fails to parse everything specified by the format string, or if insufficient information is parsed to specify a complete duration, time point, or calendrical data structure, `setstate(ios_base::failbit)` is called on the `basic_istream`.

   > -?- The sequence of characters extracted for those specifiers that are described as depending on a locale are implementation-defined.

   > Table 134 — Meaning of parse flags [tab:time.parse.spec]

   > […]

### 4542(i). Resuming a coroutine on a different execution agent that is an instance of a `std::thread` is underspecified

Section: 17.13.4.6 [coroutine.handle.resumption] Status: Tentatively NAD Submitter: jim x Opened: 2026-03-11 Last modified: 2026-04-13

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Consider the following example:

>     #include <atomic>
>     #include <cassert>
>     #include <coroutine>
>     #include <thread>

>     std::atomic<bool> flag = false;
>     int global = 0;

>     struct Task
>     {
>       struct promise_type
>       {
>         Task get_return_object() {
>           return {std::coroutine_handle<promise_type>::from_promise(*this)};
>         }
>         std::suspend_never initial_suspend() { return {}; }
>         std::suspend_never final_suspend() noexcept { return {}; }
>         void return_void() {}
>         void unhandled_exception() {}
>       };

>       std::coroutine_handle<promise_type> handle;

>       Task(std::coroutine_handle<promise_type> h) : handle(h) {}
>       ~Task() {}
>     };

>     struct SuspendOnce
>     {
>       bool suspended = false;
>       bool await_ready() { return suspended; }
>       void await_suspend(std::coroutine_handle<>) { suspended = true; }
>       void await_resume() {}
>     };

>     Task my_coroutine() {
>       co_await SuspendOnce{};
>       flag.store(true, std::memory_order::release); // #2
>     }

>     auto t1 = std::thread([]() {
>       while (!flag.load(std::memory_order::acquire)); //#3
>       assert(global == 1); // #4
>     });

>     int main() {
>       auto task = my_coroutine();
>       auto& h = task.handle;
>       auto t2 = std::thread([&]() {
>         global = 1; // #1
>         h.resume();
>       });
>       t2.join();
>       t1.join();
>     }

17.13.4.6 [coroutine.handle.resumption] p1 says:

> Resuming a coroutine via `resume`, `operator()`, or `destroy` on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`.

The wording doesn't specify the behavior for the latter case. We want `#1` to be sequenced before `#2`.

The sequenced-before is described in terms of two evaluations that are both executed by a single thread. The coroutine is initially executed in the main thread; however, it's resumed in a different thread. The first paragraph doesn't specify the specific behavior; it only says the behavior is implementation-defined if the different execution agent is not an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`.

[2026-04-13; Reflector poll. Status → Tentatively NAD.]

The point of paragraph 1 is to say when paragraphs 2-5 don't apply (e.g. if you suspend on the main thread and resume on a fiber). If you don't do anything like that, then paragraphs 2-5 describe the effects.

Proposed resolution:

This wording is relative to N5032.

1. Modify 17.13.4.6 [coroutine.handle.resumption] as indicated:

   > -1- Resuming a coroutine via `resume`, `operator()`, or `destroy` on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`. Otherwise, the execution of the coroutine is resumed, or the coroutine is destroyed, on the execution agent on which the corresponding member function is invoked.

   > […]

### 4543(i). Incorrect cast between `simd::vec` and `simd::mask` via conversion to and from impl-defined vector types

Section: 29.10.7 [simd.class], 29.10.9 [simd.mask.class] Status: New Submitter: Matthias Kretz Opened: 2026-03-16 Last modified: 2026-04-13

Priority: 2

View all issues with New status.

Discussion:

Consider:

>     simd::vec<int> x = 1;
>     simd::vec<short> a = x; // ill-formed & concise error 👍
>     auto b = static_cast<simd::vec<short>>(x); // might compile or fail with pages of errors

>     simd::mask<complex<float>> k = true;
>     auto c = static_cast<simd::mask<float>>(k); // might compile or fail with pages of errors

Why can `b` and `c` sometimes work? It depends on the implementation, whether and how it implements the "recommended practice" of implicit conversion to and from implementation-defined non-standard vector types.

E.g. `vec<int>` could be convertible to `__m256i` and `__m256i` is then convertible to `vec<short>`. Likewise, `mask<complex<float>>` could be implemented as a `mask<float>` internally, making it convertible to `__vector(8) int`, the same type that `mask<float>` uses.

If the implicit conversion constructor is implemented as a constrained template, there's no problem. However, a non-template such as e.g.

>     basic_mask(__m256i)

enables the conversion sequence from another `basic_vec` or `basic_mask`.

Given an enabled `basic_vec` specialization `V` the following must always hold:

>     constructible_from<V, simd::rebind_t<int, V>>
>     constructible_from<V, simd::rebind_t<float, V>>
>     !constructible_from<V, simd::resize_t<V::size() + 1, V>>
>     !constructible_from<V, simd::resize_t<V::size() + 1, typename V::mask_type>>
>     !constructible_from<typename V::mask_type, V>

Given an enabled `basic_mask` specialization `M` the following must always hold:

>     std::constructible_from<M, simd::rebind_t<int, M>>
>     std::constructible_from<M, simd::rebind_t<float, M>>
>     !std::constructible_from<M, simd::resize_t<M::size() + 1, M>>

There are two possible solutions:

1. delete all unwanted conversions explicitly or

2. require the "recommended practice" conversion constructors to use a constrained template.

I strongly favor the first solution because it not only removes the incorrect conversions but also reduces the error message to something understandable, additionally allowing a message from the developer via `= delete("wrong!")`.

Proposed resolution: Adopt P4042R0

[2026-04-13; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

### 4545(i). Some converting constructors of `layout_left_padded::mapping` and `layout_right_padded::mapping` are missing a representability precondition for the padded stride

Section: 23.7.3.4.8.3 [mdspan.layout.leftpad.cons], 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] Status: New Submitter: Xi Chen Opened: 2026-03-18 Last modified: 2026-05-29

Priority: 3

View all issues with New status.

Discussion:

It appears that these constructors are missing a precondition requiring the incoming padded stride to be representable as a value of the destination `index_type`.

The existing preconditions check that `other.required_span_size()` is representable as a value of type `index_type`, but that is not sufficient when the source mapping is empty. In that case, `other.required_span_size()` can be `0` even though the source padded stride is too large for the destination `index_type`. The padded stride is then silently truncated, and that truncation is observable through `stride()`.

Example:

>     #include <mdspan>
>     #include <cstdint>
>     #include <cassert>

>     void test() {
>       using Src = std::layout_left_padded<std::dynamic_extent>::mapping<std::extents<uint32_t, 1, 0>>;
>       using Dst = std::layout_left_padded<std::dynamic_extent>::mapping<std::extents<uint8_t, 1, 0>>;

>       // src.stride(1) == 256.
>       Src src(std::extents<uint32_t, 1, 0>(), 256);

>       // other.required_span_size() is 0, so the existing preconditions are met.
>       // The stored padded stride is then converted to uint8_t and truncated to 0.
>       Dst dst(src);

>       assert(dst.stride(1) == 0);
>     }

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

There was split regarding P3 or P4. The `layout_stride` also require only `required_span_size` to be representable, allowing `strides` to overflow.

Proposed resolution:

This wording is relative to N5032.

1. Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:

   >     template<class OtherExtents>
   >       constexpr explicit(see below)
   >         mapping(const layout_stride::mapping<OtherExtents>& other);

   > > -10- Constraints: […]

   > > -11- Preconditions:

   > > - (11.1) — […]

   > > - (11.2) — […]

   > > - (11.3) — […]

   > > - (11.4) — `other.required_span_size()` is representable as a value of type `index_type`.

   > > - (11.5) — If `rank_` is greater than `1` and `static-padding-stride` equals `dynamic_extent`, `other.stride(1)` is representable as a value of type `index_type`.

   > > -12- Effects: […]

   > > -13- Remarks: […]

   >     template<class LayoutLeftPaddedMapping>
   >       constexpr explicit(see below)
   >         mapping(const LayoutLeftPaddedMapping& other);

   > > -14- Constraints: […]

   > > -15- Mandates: […]

   > > -16- Preconditions:

   > > - (16.1) — […]

   > > - (16.2) — `other.required_span_size()` is representable as a value of type `index_type`.

   > > - (16.3) — If `rank_` is greater than `1` and `static-padding-stride` equals `dynamic_extent`, `other.stride(1)` is representable as a value of type `index_type`.

   > > -17- Effects: […]

   > > -18- Remarks: […]

2. Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:

   >     template<class OtherExtents>
   >       constexpr explicit(see below)
   >         mapping(const layout_stride::mapping<OtherExtents>& other);

   > > -10- Constraints: […]

   > > -11- Preconditions:

   > > - (11.1) — […]

   > > - (11.2) — […]

   > > - (11.3) — […]

   > > - (11.4) — `other.required_span_size()` is representable as a value of type `index_type`.

   > > - (11.5) — If `rank_` is greater than `1` and `static-padding-stride` equals `dynamic_extent`, `other.stride(rank_ - 2)` is representable as a value of type `index_type`.

   > > -12- Effects: […]

   > > -13- Remarks: […]

   >     template<class LayoutRightPaddedMapping>
   >       constexpr explicit(see below)
   >         mapping(const LayoutRightPaddedMapping& other);

   > > -14- Constraints: […]

   > > -15- Mandates: […]

   > > -16- Preconditions:

   > > - (16.1) — […]

   > > - (16.2) — `other.required_span_size()` is representable as a value of type `index_type`.

   > > - (16.3) — If `rank_` is greater than `1` and `static-padding-stride` equals `dynamic_extent`, `other.stride(rank_ - 2)` is representable as a value of type `index_type`.

   > > -17- Effects: […]

   > > -18- Remarks: […]

### 4546(i). Integer-class type should be structural type

Section: 24.3.4.4 [iterator.concept.winc] Status: New Submitter: Hewill Kang Opened: 2026-03-19 Last modified: 2026-05-29

Priority: 3

View other active issues in [iterator.concept.winc].

View all other issues in [iterator.concept.winc].

View all issues with New status.

Discussion:

Currently, integer-class types are not guaranteed to be structure types, so the range of `size` members that return integer-class types may not be able to model `tiny-range`, because its size value cannot be used as a template parameter even if it is a constant expression. This does not appear to be the intention.

Given that integer-class types should behave like regular integers, and those types in MSVC-STL and libstdc++ are both implemented as structural types, it would be better to be clear about the wording.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

Need additional statement clarifying when the types are template-argument-equivalent.

Proposed resolution:

This wording is relative to N5032.

1. Modify 24.3.4.4 [iterator.concept.winc] as indicated:

   > -9- All integer-class types are structural types (13.2 [temp.param]) that model `regular` (18.6 [concepts.object]) and `three_way_comparable<strong_ordering>` (17.12.4 [cmp.concept]).

### 4547(i). `sender-awaitable` needs a deduction guide

Section: 33.13.1 [exec.as.awaitable] Status: Ready Submitter: Eric Niebler Opened: 2026-03-19 Last modified: 2026-06-09

Priority: 2

View all other issues in [exec.as.awaitable].

View all issues with Ready status.

Discussion:

33.13.1 [exec.as.awaitable] p2 has the following class declaration for the exposition-only `sender-awaitable` type:

>     template<class Sndr, class Promise>
>     class sender-awaitable {
>       struct unit {};                                             // exposition only
>       using value-type =                                          // exposition only
>         single-sender-value-type<Sndr, env_of_t<Promise>>;
>       using result-type =                                         // exposition only
>         conditional_t<is_void_v<value-type>, unit, value-type>;
>       struct awaitable-receiver;                                  // exposition only

>       variant<monostate, result-type, exception_ptr> result{};    // exposition only
>       connect_result_t<Sndr, awaitable-receiver> state;           // exposition only

>     public:
>       sender-awaitable(Sndr&& sndr, Promise& p);
>       static constexpr bool await_ready() noexcept { return false; }
>       void await_suspend(coroutine_handle<Promise>) noexcept { start(state); }
>       value-type await_resume();
>     };

In 33.13.1 [exec.as.awaitable] p2, CTAD is used to deduce the template parameters of `sender-awaitable` in the expressions `sender-awaitable{adapted-expr, p}` and `sender-awaitable{expr, p}`.

If `decltype(expr)` is a type `Expr&` (i.e., `expr` is an lvalue subexpression), then CTAD will deduce the type as `sender-awaitable<Expr, Promise>` and the expression `sender-awaitable{expr, p}` will be ill-formed because it is trying to bind an lvalue subexpression to an rvalue reference parameter.

The intention was that the template parameter `Sndr` would be deduced preserving the cv- and reference-qualification of the sender subexpression. In order to achieve that, we need to add a deduction guide.

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

There were only 3 votes, but all in favor of P0.

[Brno 2026-06-08; Change status New → Ready.]

Proposed resolution:

This wording is relative to N5032.

1. Modify 33.13.1 [exec.as.awaitable] as indicated:

   > -2- The type `sender-awaitable<Sndr, Promise>` is equivalent to:

   > >     namespace std::execution {
   > >       template<class Sndr, class Promise>
   > >       class sender-awaitable {
   > >         struct unit {};                                             // exposition only
   > >         using value-type =                                          // exposition only
   > >           single-sender-value-type<Sndr, env_of_t<Promise>>;
   > >         using result-type =                                         // exposition only
   > >           conditional_t<is_void_v<value-type>, unit, value-type>;
   > >         struct awaitable-receiver;                                  // exposition only

   > >         variant<monostate, result-type, exception_ptr> result{};    // exposition only
   > >         connect_result_t<Sndr, awaitable-receiver> state;           // exposition only

   > >       public:
   > >         sender-awaitable(Sndr&& sndr, Promise& p);
   > >         static constexpr bool await_ready() noexcept { return false; }
   > >         void await_suspend(coroutine_handle<Promise>) noexcept { start(state); }
   > >         value-type await_resume();
   > >       };

   > >       template<class Sndr, class Promise>
   > >         sender-awaitable(Sndr&&, Promise&) -> sender-awaitable<Sndr, Promise>;
   > >     }

### 4551(i). Fix the customization domain of the `on` algorithm

Section: 33.9.12.8 [exec.on] Status: New Submitter: Eric Niebler Opened: 2026-03-23 Last modified: 2026-05-29

Priority: 2

View all issues with New status.

Discussion:

As currently specified, for the expression `on(sch, sndr)`, the starting domain is the receiver's domain, and the (`set_value`) completion domain is `sndr`'s completion domain, given that it starts on `sch`. Those are the two domains that will be used to find customizations of `on`, neither of which is the domain of `sch`.

Presumably, if a user wants to customize `on`, they would expect `on(sch, sndr)` to use the domain of `sch` to find the customization. So there is a gap between what would be least surprising and what is currently specified.

In P3826R5, we added a `get_completion_domain<T>` query where `T` could be either a completion tag (`set_value_t`, et. al) or `void` (and defaults to `void`), where `get_completion_domain<void>` defaults to `get_completion_domain<set_value_t>`.

`get_completion_domain<void>(get_env(sndr), env)` is the domain that gets used by `transform_sender` to find a `set_value` sender transform. In other words, `transform_sender(sndr, env)` will, among other things, apply the following transform:

>     auto sndr2 = get_completion_domain<>(get_env(sndr), env)
>                     .transform_sender(set_value, forward<Sndr>(sndr), env);

We can use this to query to make `transform_sender` use the domain of `sch` to find a customization of `on(sch, sndr)`.

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

Proposed resolution:

This wording is relative to N5032.

1. Modify 33.9.12.8 [exec.on] as indicated:

   > -9- Let `out_sndr` be a subexpression denoting a sender returned from `on(sch, sndr)` or one equal to such, and let `OutSndr` be the type `decltype((out_sndr))`. For a pack of subexpressions `env`, `get_completion_domain<>(get_env(out_sndr), env...)` is expression-equivalent to `get_completion_domain<set_value_t>(sch, env...)`. Let `out_rcvr` be a subexpression denoting a receiver that has an environment of type `Env` such that `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to the operation state that results from connecting `out_sndr` with `out_rcvr`. Calling `start(op)` shall […]

### 4558(i). `drop_view` and `drop_while_view` should be `const`-iterable for input-only range

Section: 25.7.12.2 [range.drop.view], 25.7.13.2 [range.drop.while.view] Status: LEWG Submitter: Hewill Kang Opened: 2026-03-25 Last modified: 2026-05-30

Priority: Not Prioritized

View other active issues in [range.drop.view].

View all other issues in [range.drop.view].

View all issues with LEWG status.

Discussion:

After P3725R3, `filter_view` can be `const`-iterable as long as the underlying range is input-only range, implying that LEWG do believe that providing `const`-iterable properties for views does have some value.

Its turn out that `drop_view` and `drop_while_view` are also `const`-iterable in the same situation, because they do not internally need to cache the `begin` call on the first iteration.

[2026-05-29 LWG reflector poll; status: New → LEWG]

Proposed resolution:

This wording is relative to N5032.

1. Modify 25.7.12.2 [range.drop.view] as indicated:

   > >     namespace std::ranges {
   > >       template<view V>
   > >       class drop_view : public view_interface<drop_view<V>> {
   > >       public:
   > >         […]
   > >         constexpr auto begin()
   > >           requires (!(simple-view<V> &&
   > >                       ((random_access_range<const V> && sized_range<const V>) ||
   > >                        (input_range<const V> && !forward_range<const V>))));
   > >         constexpr auto begin() const
   > >           requires (random_access_range<const V> && sized_range<const V>) ||
   > >                    (input_range<const V> && !forward_range<const V>);
   > >         […]
   > >       };
   > >       […]
   > >     }

   >     constexpr auto begin()
   >        requires (!(simple-view<V> &&
   >                    ((random_access_range<const V> && sized_range<const V>) ||
   >                     (input_range<const V> && !forward_range<const V>))));
   >     constexpr auto begin() const
   >       requires (random_access_range<const V> && sized_range<const V>) ||
   >                (input_range<const V> && !forward_range<const V>);

   > > -3- Returns: `ranges::next(ranges::begin(base_), count_, ranges::end(base_))`.

   > > -4- Remarks: In order to provide the amortized constant-time complexity required by the `range` concept when `drop_view` models `forward_range`, the first overload caches the result within the `drop_view` for use on subsequent calls.

2. Modify 25.7.13.2 [range.drop.while.view] as indicated:

   > >     namespace std::ranges {
   > >       template<view V, class Pred>
   > >         requires input_range<V> && is_object_v<Pred> &&
   > >                  indirect_unary_predicate<const Pred, iterator_t<V>>
   > >       class drop_while_view : public view_interface<drop_while_view<V, Pred>> {
   > >       public:
   > >         […]

   > >         constexpr auto begin()
   > >           requires (!(simple-view<V> &&
   > >                       input_range<const V> && !forward_range<const V>));
   > >         constexpr auto begin() const
   > >           requires (input_range<const V> && !forward_range<const V> &&
   > >                     indirect_unary_predicate<const Pred, iterator_t<const V>>);

   > >         constexpr auto end() requires (!simple-view<V>) { return ranges::end(base_); }
   > >         constexpr auto end() const requires range<const V> { return ranges::end(base_); }

   > >         […]
   > >       };
   > >       […]
   > >     }

   > […]

   >     constexpr auto begin()
   >       requires (!(simple-view<V> &&
   >                   input_range<const V> && !forward_range<const V>));
   >     constexpr auto begin() const
   >       requires (input_range<const V> && !forward_range<const V> &&
   >                 indirect_unary_predicate<const Pred, iterator_t<const V>>);

   > > -3-Preconditions: `pred_.has_value()` is `true`.

   > > -4-Returns: `ranges::find_if_not(base_, cref(*pred_))`.

   > > -5-Remarks: In order to provide the amortized constant-time complexity required by the `range` concept when `drop_while_view` models `forward_range`, the first overloadcall caches the result within the `drop_while_view` for use on subsequent calls.

### 4559(i). `zip_view`, `zip_transform_view`, and `cartesian_product_view` should provide `reserve_hint`

Section: 25.7.25.2 [range.zip.view], 25.7.26.2 [range.zip.transform.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2026-03-27 Last modified: 2026-05-29

Priority: 3

View other active issues in [range.zip.view].

View all other issues in [range.zip.view].

View all issues with New status.

Discussion:

Although the original paper P2846 gives a reason for not providing `reserve_hint` for these three views — namely, uncertainty about what the best approach is — it seems to me that providing them is indeed intuitively unambiguous. We don't need to consider the inconsistency between `const` and non-`const` overloads because it's not within reasonable ranges.

Given that we provided `reserve_hint` for `concat_view` to resolve NB comment FR-025-246, it seems that only these three views are available, but not provided; it's time to do so.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

Concerns with `cartesian_product` as any overestimation will be amplified exponentially. P0 votes for just the `zip` change.

Proposed resolution:

This wording is relative to N5032.

1. Modify 25.7.25.2 [range.zip.view] as indicated:

   > >     namespace std::ranges {
   > >       […]
   > >       template<input_range... Views>
   > >         requires (view<Views> && ...) && (sizeof...(Views) > 0)
   > >       class zip_view : public view_interface<zip_view<Views...>> {
   > >         […]
   > >         constexpr auto size() requires (sized_range<Views> && ...);
   > >         constexpr auto size() const requires (sized_range<const Views> && ...);

   > >         constexpr auto reserve_hint() requires (approximately_sized_range<Views> && ...);
   > >         constexpr auto reserve_hint() const requires (approximately_sized_range<const Views> && ...);
   > >       };
   > >       […]
   > >     }

   > […]

   >     constexpr auto size() requires (sized_range<Views> && ...);
   >     constexpr auto size() const requires (sized_range<const Views> && ...);

   > > -3- Effects: Equivalent to:

   > > >     return apply([](auto... sizes) {
   > > >       using CT = make-unsigned-like-t<common_type_t<decltype(sizes)...>>;
   > > >       return ranges::min({CT(sizes)...});
   > > >     }, tuple-transform(ranges::size, views_));

   >     constexpr auto reserve_hint() requires (approximately_sized_range<Views> && ...);
   >     constexpr auto reserve_hint() const requires (approximately_sized_range<const Views> && ...);

   > > -?- Effects: Equivalent to:

   > > >     return apply([](auto... sizes) {
   > > >       using CT = make-unsigned-like-t<common_type_t<decltype(sizes)...>>;
   > > >       return ranges::min({CT(sizes)...});
   > > >     }, tuple-transform(ranges::reserve_hint, views_));

2. Modify 25.7.26.2 [range.zip.transform.view] as indicated:

   > >     namespace std::ranges {
   > >       […]
   > >       namespace std::ranges {
   > >       template<move_constructible F, input_range... Views>
   > >         requires (view<Views> && ...) && (sizeof...(Views) > 0) && is_object_v<F> &&
   > >                   regular_invocable<F&, range_reference_t<Views>...> &&
   > >                   can-reference<invoke_result_t<F&, range_reference_t<Views>...>>
   > >       class zip_transform_view : public view_interface<zip_transform_view<F, Views...>> {
   > >         […]
   > >         constexpr auto size() requires sized_range<InnerView> {
   > >           return zip_.size();
   > >         }

   > >         constexpr auto size() const requires sized_range<const InnerView> {
   > >           return zip_.size();
   > >         }

   > >         constexpr auto reserve_hint() requires approximately_sized_range<InnerView> {
   > >           return zip_.reserve_hint();
   > >         }

   > >         constexpr auto reserve_hint() const requires approximately_sized_range<const InnerView> {
   > >           return zip_.reserve_hint();
   > >         }
   > >       };
   > >       […]
   > >     }

   > […]

3. Modify 25.7.33.2 [range.cartesian.view] as indicated:

   > >     namespace std::ranges {
   > >       […]
   > >       template<class... Vs>
   > >       concept cartesian-product-is-sized =                  // exposition only
   > >         (sized_range<Vs> && ...);

   > >       template<class... Vs>
   > >       concept cartesian-product-is-approximately-sized =    // exposition only
   > >         (approximately_sized_range<Vs> && ...);
   > >       […]

   > >       template<input_range First, forward_range... Vs>
   > >         requires (view<First> && ... && view<Vs>)
   > >       class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> {
   > >         […]
   > >         constexpr see below size()
   > >           requires cartesian-product-is-sized<First, Vs...>;
   > >         constexpr see below size() const
   > >           requires cartesian-product-is-sized<const First, const Vs...>;

   > >         constexpr see below reserve_hint()
   > >           requires cartesian-product-is-approximately-sized<First, Vs...>;
   > >         constexpr see below reserve_hint() const
   > >           requires cartesian-product-is-approximately-sized<const First, const Vs...>;
   > >       };
   > >       […]
   > >     }

   > […]

   >     constexpr see below size()
   >       requires cartesian-product-is-sized<First, Vs...>;
   >     constexpr see below size() const
   >       requires cartesian-product-is-sized<const First, const Vs...>;

   > > -7- The return type is an implementation-defined unsigned-integer-like type.

   > > -8- Recommended practice: The return type should be the smallest unsigned-integer-like type that is sufficiently wide to store the product of the maximum sizes of all the underlying ranges, if such a type exists.

   > > -9- Let p be the product of the sizes of all the ranges in `bases_`.

   > > -10- Preconditions: p can be represented by the return type.

   > > -11- Returns: p.

   >     constexpr see below reserve_hint()
   >       requires cartesian-product-is-approximately-sized<First, Vs...>;
   >     constexpr see below reserve_hint() const
   >       requires cartesian-product-is-approximately-sized<const First, const Vs...>;

   > > -?- The return type is an implementation-defined unsigned-integer-like type.

   > > -?- Recommended practice: The return type should be the smallest unsigned-integer-like type that is sufficiently wide to store the product of the maximum approximately sizes of all the underlying ranges, if such a type exists.

   > > -?- Let p be the product of the approximately sizes of all the ranges in `bases_`.

   > > -?- Preconditions: p can be represented by the return type.

   > > -?- Returns: p.

### 4560(i). Explicitly specify effects of precision specification in `duration` formatter

Section: 30.12 [time.format] Status: LEWG Submitter: Liang Jiaming Opened: 2026-03-27 Last modified: 2026-05-30

Priority: Not Prioritized

View other active issues in [time.format].

View all other issues in [time.format].

View all issues with LEWG status.

Discussion:

Currently, the formatter for chrono types handles fill-align-width-precision besides chrono specifications. On the one hand, the standard regulates in the second paragraph of 30.12 [time.format] p1 that:

> The productions fill-and-align, width, and precision are described in 28.5.2 [format.string].

and in 30.12 [time.format] p7:

> Unless otherwise specified, if the chrono-specs is omitted, the chrono object is formatted as if by streaming it to `basic_ostringstream<charT>` `os` with the formatting locale imbued and copying `os.str()` through the output iterator of the context with additional padding and adjustments as specified by the format specifiers.

> [Example 3:

>     string s = format("{:=>8}", 42ms);      // value of s is "====42ms"

> — end example]

which imply that the precision specification takes effects on the final formatted string. On the other hand, the next two statements in 30.12 [time.format] p1 say that:

> Giving a precision specification in the chrono-format-spec is valid only for types that are specializations of `std::chrono::duration` for which the nested typedef-name `rep` denotes a floating-point type. For all other types, an exception of type `format_error` is thrown if the chrono-format-spec contains a precision specification.

which imply that the precision specification takes effects on the underlying floating-point representation.

These two contradictory intentions make unnecessary confusion and divergence in different implementations. Given code example below:

>     std::chrono::duration<float> s = 10ms + 10us + 123ns;
>     std::print("from chrono {:.3} | from count {:.3%Q}\n", s);

It gives four different outputs in libstdc++, libc++, MS-STL and FMT (i.e. fmtlib/fmt; though it doesn't need to be standard-compliant, we list it here for comparison):

> Implementation | `{0:.3}` | `{0:.3%Q}`
> --- | --- | ---
> libc++ | `"0.0"` | `"0.010010123"`
> libstdc++ | `"0.0100101s"` | `"0.010010123"`
> MS-STL | `"0.0100101s"` | `"0.0100101"`
> FMT | `"0.010s"` | `"0.010"`

To be specific, the current implementations are as follows:

1. No chrono-spec, with precision:

   - libc++: Strictly obey 30.12 [time.format] p7, so the streamed string is truncated as if by `format("{:.3}", "0.0100101s")` and remains the first three characters.

   - libstdc++: Ignore the precision.

   - MS-STL: Ignore the precision.

   - FMT: Apply the precision on the underlying floating-point representation.

2. `%Q` spec, with precision:

   - libc++: Ignore the precision and format representation to string. Note that `format` will apply `to_chars` on floating-point numbers, which forms the shortest recoverable string so `0.010010123` is fully output.

   - libstdc++: Ignore the precision and format representation to string.

   - MS-STL: Ignore the precision, output count to stream, and format the streamed string. So it outputs 6 significant digits and gives `0.0100101`.

   - FMT: Apply the precision on the underlying floating-point representation.

To bridge the gap among implementations, the standard should explicitly specify the effects of precision. Since the current wording says that precision is only allowed for `duration` with floating-point representation, an intuitive solution is just applying precision on that representation. Otherwise (i.e. the precision is applied on the streamed string), other chrono types like `time_point` should be capable of handling that too, making current constraint seemingly unnecessary.

[2026-05-29 LWG telecon; status: New → LEWG]

This is related to P2945 and P3148.

Proposed resolution:

This wording is relative to N5032.

1. Modify 30.12 [time.format] as indicated:

   > -1- Each `formatter` (28.5.6 [format.formatter]) specialization in the chrono library (30.2 [time.syn]) meets the Formatter requirements (28.5.6.1 [formatter.requirements]). The `parse` member functions of these formatters interpret the format specification as a chrono-format-spec according to the following syntax:

   > […]

   > The productions fill-and-align, width, and precision are described in 28.5.2 [format.string]. Giving a precision specification in the chrono-format-spec is valid only for types that are specializations of `std::chrono::duration` for which the nested typedef-name `rep` denotes a floating-point type, and the precision specification doesn't take effects unless explicitly specified. For all other types, an exception of type `format_error` is thrown if the chrono-format-spec contains a precision specification. All ordinary multibyte characters represented by literal-char are copied unchanged to the output.

   > […]

   > -7- Unless otherwise specified, if the chrono-specs is omitted, the chrono object is formatted as if by streaming it to `basic_ostringstream<charT> os` with the formatting locale imbued and copying `os.str()` through the output iterator of the context with additional padding and adjustments as specified by the format specifiers. The precision specification is applied as if by `os << fixed << setprecision(precision)`.

2. Modify Table 133 — "Meaning of conversion specifiers" [tab:time.format.spec] as indicated:

   Table 133 — Meaning of conversion specifiers [tab:time.format.spec]
   Specifier | Replacement
   --- | ---
   `[…]`
   `%Q` | The `duration`'s numeric value (as if extracted via `.count()`). The precision specification is applied on that numeric value.
   `[…]`

### 4561(i). `reverse_view` does not consider `simple-view`

Section: 25.7.21.2 [range.reverse.view] Status: New Submitter: Hewill Kang Opened: 2026-03-27 Last modified: 2026-05-29

Priority: 3

View other active issues in [range.reverse.view].

View all other issues in [range.reverse.view].

View all issues with New status.

Discussion:

Currently, non-`const` `begin`/`end` for `reverse_view` are always provided.

However, when the underlying range is `simple-view` and `common_view`, we can just provide const `begin` to reduce the number of template instantiations.

And it is also reasonable to only provide `const` end in the case of `simple-view` for the same reason.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

The discussion concluded that this is not user-observable change, as we assume that if view `begin()` and `begin() const` return the same type, then their behavior is equivalent.

Proposed resolution:

This wording is relative to N5032.

1. Modify 25.7.21.2 [range.reverse.view] as indicated:

   > >     namespace std::ranges {
   > >       template<view V>
   > >         requires bidirectional_range<V>
   > >       class reverse_view : public view_interface<reverse_view<V>> {
   > >         […]
   > >         constexpr reverse_iterator<iterator_t<V>> begin()
   > >           requires (!(simple-view<V> && common_range<const V>));
   > >         constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>;
   > >         constexpr auto begin() const requires common_range<const V>;

   > >         constexpr reverse_iterator<iterator_t<V>> end() requires (!simple-view<V>);
   > >         constexpr auto end() const requires common_range<const V>;
   > >         […]
   > >       };
   > >       […]
   > >     }

   > […]

   >     constexpr reverse_iterator<iterator_t<V>> begin()
   >       requires (!(simple-view<V> && common_range<const V>));

   > > -2-Returns:

   > > >     make_reverse_iterator(ranges::next(ranges::begin(base_), ranges::end(base_)))

   > > -3- Remarks: In order to provide the amortized constant time complexity required by the `range` concept, this function caches the result within the `reverse_view` for use on subsequent calls.

   >     constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>;
   >     constexpr auto begin() const requires common_range<const V>;

   > > -4- Effects: Equivalent to: `return make_reverse_iterator(ranges::end(base_));`

   >     constexpr reverse_iterator<iterator_t<V>> end() requires (!simple-view<V>);
   >     constexpr auto end() const requires common_range<const V>;

   > > -5- Effects: Equivalent to: `return make_reverse_iterator(ranges::begin(base_));`

### 4562(i). `layout_stride::is_exhaustive` specification is problematic when there's an extent equal to 1

Section: 23.7.3.4.7.4 [mdspan.layout.stride.obs] Status: Ready Submitter: S. B. Tam Opened: 2026-03-29 Last modified: 2026-06-11

Priority: 3

View all issues with Ready status.

Discussion:

Consider

>     constexpr std::extents ext{2, 1, 2};
>     constexpr std::array stride{1, 5, 2};
>     constexpr std::layout_stride::mapping lsm(ext, stride);

>     static_assert(lsm.is_exhaustive());

Implementations disagree on the result of `lsm.is_exhaustive()`: `true` on libstdc++ and libc++, `false` on MSVC STL.

github.com/microsoft/STL/#5477 claims that the mapping is exhaustive in this case, but 23.7.3.4.7.4 [mdspan.layout.stride.obs]/6.2 requires this function to return `false`.

Should `is_exhaustive` be made to return `true` in this case (which is more correct and also easier to implement)?

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

[2026-06-10; Tomasz provides new wording]

Reflect implementation in the wording.

[Brno 2026-06-11; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N5008.

1. Modify 23.7.3.4.7.4 [mdspan.layout.stride.obs] as indicated:

   >     constexpr bool is_exhaustive() const noexcept;

   > > -5- Returns: `required_span_size() == extents().fwd-prod-of-extents(rank_)`.

   > > 1. (5.1) — `true` if `rank_` or the size of the multidimensional index space `m.extents()` is `0`.

   > > 2. (5.2) — Otherwise, `true` if there is a permutation `P` of the integers in the range `[0, rank_)` such that `stride(p0)` equals `1`, and `stride(pi)` equals `stride(pi−1) * extents().extent(pi−1)` for i in the range `[1, rank_)`, where `pi` is the ith element of `P`.

   > > 3. (5.3) — Otherwise, `false`.

### 4563(i). Missing "round to nearest, ties away from zero" mode

Section: 17.3.4 [round.style] Status: LEWG Submitter: Jan Schultke Opened: 2026-03-30 Last modified: 2026-05-30

Priority: Not Prioritized

View all other issues in [round.style].

View all issues with LEWG status.

Discussion:

As discussed in LWG 4474(i) (approved for C++26), `std::round_style` is intended to be symmetrical with `FLT_ROUNDS`. LWG 4474(i) clarified that `round_to_nearest` corresponds to an `FLT_ROUNDS` value of 1, meaning "to nearest, ties to even".

Unfortunately, any implementation using the mode "to nearest, ties away from zero" (corresponding to an `FLT_ROUNDS` value of 4 and supported by ISO/IEC 60559 floating-point implementations) is forced to stop using `round_to_nearest` as a `numeric_limits<float>::round_style` constant due to stricter requirements for that enumerator. To prevent such implementations from resorting to a non-standard enumerator, a new enumerator is needed.

The name should be inspired by C23 macros such as `FE_TONEARESTFROMZERO`.

See also ISO/IEC 9899:2024 §5.2.5.3 paragraph 23

[2026-05-29 LWG telecon; status: New → LEWG]

Proposed resolution:

This wording is relative to N5032 after application of LWG motion 1 (See LWG Motion 1-#4474).

1. Modify 17.3.4 [round.style] as indicated:

   > >     namespace std {
   > >       enum float_round_style {
   > >         round_indeterminate = -1,
   > >         round_toward_zero = 0,
   > >         round_to_nearest = 1,
   > >         round_toward_infinity = 2,
   > >         round_toward_neg_infinity = 3,
   > >         round_to_nearest_from_zero = 4
   > >       };
   > >     }

   > > -1- The rounding mode for floating-point arithmetic is characterized by the values:

   > > - (1.1) — `round_indeterminate` if the rounding style is indeterminable

   > > - (1.2) — `round_toward_zero` if the rounding style is toward zero

   > > - (1.3) — `round_to_nearest` if the rounding style is to the nearest representable value; if there are two equally near such values, the one with an even least significant digit is chosen

   > > - (1.4) — `round_toward_infinity` if the rounding style is toward infinity

   > > - (1.5) — `round_toward_neg_infinity` if the rounding style is toward negative infinity

   > > - (1.?) — `round_to_nearest_from_zero` if the rounding style is to the nearest representable value; if there are two equally near such values, the one with greater magnitude is chosen

### 4564(i). Clarify when `constant_of` throws

Section: 21.4.7 [meta.reflection.queries] Status: New Submitter: Marek Polacek Opened: 2026-03-31 Last modified: 2026-05-29

Priority: 2

View other active issues in [meta.reflection.queries].

View all other issues in [meta.reflection.queries].

View all issues with New status.

Discussion:

The `constant_of` specification in 21.4.7 [meta.reflection.queries] p9 says:

> Throws: `meta::exception` unless either `r` represents an annotation or `[: R :]` is a valid splice-expression (7.5.9 [expr.prim.splice]).

But if we have `[:^^X:]` where `X` is a template, the splice-expression is valid, but we should still throw, because `constant_of` on a template declaration makes no sense. Since we call `reflect_constant` on the splice as per:

>     if constexpr (is_annotation(R)) {
>       return C;
>     } else if constexpr (is_array_type(type_of(R))) {
>       return reflect_constant_array([: R :]);
>     } else if constexpr (is_function_type(type_of(R))) {
>       return reflect_function([: R :]);
>     } else {
>       return reflect_constant([: R :]);
>     }

the code would be ill-formed anyway, but it seems clearer to say something like:

> Throws: `meta::exception` unless either `r` represents an annotation or `[: R :]` is a valid splice-expression that doesn't designate a template.

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

The `[:R:]` is not valid splice-expression for template. However, we need to handle direct base class relantionships and non-static data members. In this cases `reflect_constant(_array)` calls are ill-formed.

Proposed resolution:

This wording is relative to N5032.

1. Modify 21.4.7 [meta.reflection.queries] as indicated:

   >     consteval info constant_of(info r);

   > > -7- Let `R` be a constant expression of type `info` such that `R == r` is `true`. If `r` represents an annotation, then let `C` be its underlying constant.

   > > -8- Effects: Equivalent to:

   > > >     if constexpr (is_annotation(R)) {
   > > >       return C;
   > > >     } else if constexpr (is_array_type(type_of(R))) {
   > > >       return reflect_constant_array([: R :]);
   > > >     } else if constexpr (is_function_type(type_of(R))) {
   > > >       return reflect_function([: R :]);
   > > >     } else {
   > > >       return reflect_constant([: R :]);
   > > >     }

   > > -9- Throws: `meta::exception` unless either `r` represents an annotation or `[: R :]` is a valid splice-expression (7.5.9 [expr.prim.splice]) that doesn't designate a template.

### 4565(i). `subrange` should prevent slicing for contiguous iterators

Section: 25.5.4.1 [range.subrange.general] Status: New Submitter: Hewill Kang Opened: 2026-04-03 Last modified: 2026-05-29

Priority: 3

View other active issues in [range.subrange.general].

View all other issues in [range.subrange.general].

View all issues with New status.

Discussion:

Currently, the `subrange` uses the following exposition-only concept:

>     template<class From, class To>
>       concept uses-nonqualification-pointer-conversion =      // exposition only
>         is_pointer_v<From> && is_pointer_v<To> &&
>         !convertible_to<remove_pointer_t<From>(*)[], remove_pointer_t<To>(*)[]>;

to disallow derived to base conversions. However, this only applies to pointers.

Given that `subrange` is intended to universally take any iterator-pair to form a view, we may need to generally prevent this in the case of contiguous iterators (demo):

>     struct Base {};
>     struct Derived : Base {};
>     ranges::subrange<Derived*> sd;
>     ranges::subrange<Base*> sb = sd;                        // error
>     ranges::subrange<const Base*> sb = views::as_const(sd); // ok

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

"It seems like we should apply this protection to all iterator adaptors. Derived-to-base conversions are not valid for pointers that are used as iterators."

Proposed resolution:

This wording is relative to N5032.

1. Modify 25.5.4.1 [range.subrange.general] as indicated:

   >     namespace std::ranges {
   >       template<class From, class To>
   >         concept uses-nonqualification-pointer-conversion =      // exposition only
   >           is_pointer_vcontiguous_iterator<From> && is_pointer_vcontiguous_iterator<To> &&
   >           !convertible_to<remove_pointer_tremove_reference_t<iter_reference_t<From>>(*)[],
   >                           remove_pointer_tremove_reference_t<iter_reference_t<To>>(*)[]>;
   >        […]
   >     }

### 4566(i). The `element_type` definition of `scaled_accessor` and `conjugated_accessor` are not quite right

Section: 29.9.8.2 [linalg.scaled.scaledaccessor], 29.9.9.2 [linalg.conj.conjugatedaccessor] Status: New Submitter: Hewill Kang Opened: 2026-04-08 Last modified: 2026-05-29

Priority: 3

View all issues with New status.

Discussion:

The `scaled_accessor` defines `element_type` as `const decltype(declval<ScalingFactor>() * ...)`, but its `access` returns `(scaling_factor() * ...)`, where `scaling_factor()` returns `const ScalingFactor&`.

Furthermore, both `scaled_accessor` and `conjugated_accessor`'s `access` construct `NestedAccessor::element_type` via `nested-accessor.access(p, i)`, but this may not be well-formed.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

We spell explicit requirments on validity of `NestedAccessor::element_type` construction, and do not include it in the alias definition.

Proposed resolution:

This wording is relative to N5032.

1. Modify 29.9.8.2 [linalg.scaled.scaledaccessor] as indicated:

   >     namespace std::linalg {
   >       template<class ScalingFactor, class NestedAccessor>
   >       class scaled_accessor {
   >       public:
   >         using element_type =
   >           const decltype(declval<const ScalingFactor&>() *
   >                          typename declval<NestedAccessor::element_type>(
   >                             declval<typename NestedAccessor::reference>()));
   >         […]
   >       };
   >     }

2. Modify 29.9.9.2 [linalg.conj.conjugatedaccessor] as indicated:

   >     namespace std::linalg {
   >       template<class NestedAccessor>
   >       class scaled_accessor {
   >       public:
   >         using element_type =
   >           const decltype(conj-if-needed(
   >                            typename declval<NestedAccessor::element_type>(
   >                              declval<typename NestedAccessor::reference>())));
   >         […]
   >       };
   >     }

### 4569(i). `mdspan` should Mandates the relationship between `element_type` and `reference`

Section: 23.7.3.6.1 [mdspan.mdspan.overview] Status: New Submitter: Hewill Kang Opened: 2026-04-12 Last modified: 2026-05-29

Priority: 4

View other active issues in [mdspan.mdspan.overview].

View all other issues in [mdspan.mdspan.overview].

View all issues with New status.

Discussion:

For `mdspan`, user-defined accessor `A` must meet the requirements of [mdspan.accessor.reqmts]. Particularly, proxy references should model `common_reference_with<A::reference&&, A::element_type&>` to ensure that there is a relationship between these two types rather than arbitrary types.

Although `mspan` already requires that the template parameter `AccessorPolicy` shall meet the accessor policy requirements, compared to various details, such a sensible relationship between `element_type` and `reference` should be directly reflected in `mdspan`'s Mandates to prevent users from using non-compliant accessors:

>     struct BadAccessor {
>       using offset_policy = BadAccessor;
>       using element_type = int;
>       using reference    = void;
>       using data_handle_type = element_type*;
>       reference access(data_handle_type, size_t) const;
>     };

>     mdspan<int, dims<2>, layout_right, BadAccessor> m;
>     m[0, 1]; // not rejected in libstdc++, libc++ and MSVC-STL

It should be emphasized that we provided very similar wording in 25.8.3 [coro.generator.class] p1.

[2026-05-29; Reflector poll.]

Set priority to 4 after reflector poll.

This seems to only affected contrived examples, that will be ill-formed as soon as you try to use the resulting `mdspan`.

Proposed resolution:

This wording is relative to N5032.

1. Modify 23.7.3.6.1 [mdspan.mdspan.overview] as indicated:

   > >     namespace std {
   > >       template<class ElementType, class Extents, class LayoutPolicy = layout_right,
   > >                class AccessorPolicy = default_accessor<ElementType>>
   > >       class mdspan {
   > >       public:
   > >         using extents_type = Extents;
   > >         using layout_type = LayoutPolicy;
   > >         using accessor_type = AccessorPolicy;
   > >         using mapping_type = layout_type::template mapping<extents_type>;
   > >         using element_type = ElementType;
   > >         using value_type = remove_cv_t<element_type>;
   > >         using index_type = extents_type::index_type;
   > >         using size_type = extents_type::size_type;
   > >         using rank_type = extents_type::rank_type;
   > >         using data_handle_type = accessor_type::data_handle_type;
   > >         using reference = accessor_type::reference;
   > >         […]
   > >       };
   > >       […]
   > >     }

   > -2- Mandates:

   > 1. (2.1) — `ElementType` is a complete object type that is neither an abstract class type nor an array type,

   > 2. (2.2) — `Extents` is a specialization of `extents`, and

   > 3. (2.3) — `is_same_v<ElementType, typename AccessorPolicy::element_type>` is `true`, and.

   > 4. (2.?) — `common_reference_with<reference&&, element_type&>` is modeled.

### 4570(i). `COMMON-DOMAIN` should test for convertibility to `default_domain`

Section: 33.9.2 [exec.snd.expos] Status: SG1 Submitter: Eric Niebler Opened: 2026-04-13 Last modified: 2026-06-15

Priority: 3

View all other issues in [exec.snd.expos].

View all issues with SG1 status.

Discussion:

P3826R5 accepted at the Croydon meeting adds the following paragraph to 33.9.2 [exec.snd.expos]:

> For a pack of subexpressions `domains`, `COMMON-DOMAIN(domains...)` is expression-equivalent to `common_type_t<decltype(auto(domains))...>()` if that expression is well-formed, and `indeterminate_domain<Ds...>()` otherwise, where `Ds` is the pack of types consisting of `decltype(auto(domains))...` with duplicate types removed.

My intention with this paragraph was that two domains such as below would have a `COMMON-DOMAIN` of `default_domain`:

>     struct my_thread_pool_domain : std::execution::default_domain
>     {};

>     struct my_parallel_runtime_domain : std::execution::default_domain
>     {};

>     using domain-t = decltype(COMMON-DOMAIN(my_thread_pool_domain{}, my_parallel_runtime_domain{}));
>     static_assert(std::same_as<domain-t, default_domain>); // FAILS

But that's not how `common_type_t` works. Even though both domains derive from `default_domain`, they do not have a common type as determined by `common_type_t`.

We can patch this up easily enough: if `common_type_t<Domains...>` is ill-formed, we should check if `common_type_t<default_domain, Domains...>` is well-formed, this will correctly handle the case when all the domains are convertible to `default_domain`.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

The expected design regarding domain conversion is unclear, and concerns regarding special treatment of `default_domain` were raised.

[Brno 2026-06-12; Status changed: New → SG1.]

LWG would like SG1 to clarify the intended design regarding domain conversion.

Proposed resolution:

This wording is relative to N5032 after application of the wording added by P3826R5.

1. Modify 33.9.2 [exec.snd.expos] as indicated:

   > -6- For a pack of subexpressions `domains`, `COMMON-DOMAIN(domains...)` is expression-equivalent to `common_type_t<decltype(auto(domains))...>()` if that expression is well-formed,; otherwise, `common_type_t<default_domain(), decltype(auto(domains))...>()` if that expression is well-formed; and `indeterminate_domain<Ds...>()` otherwise, where `Ds` is the pack of types consisting of `decltype(auto(domains))...` with duplicate types removed.

### 4571(i). Disallow specializing `std::atomic` and `std::atomic_ref`

Section: 32.5.7.1 [atomics.ref.generic.general], 32.5.8.1 [atomics.types.generic.general] Status: New Submitter: Jiang An Opened: 2026-04-17 Last modified: 2026-05-29

Priority: 3

View all other issues in [atomics.ref.generic.general].

View all issues with New status.

Discussion:

Currently, specializations of `std::atomic` and `std::atomic_ref` for non-`bool` integer types, floating-point types, and pointer types are specified as if they're full specializations. However, implementations generally merge these specializations in quite arbitrary ways.

As for 2026-04:

- MSVC STL provides all atomic and atomic_ref specializations in primary templates.

- libc++ merges specializations for integer types with the atomic primary template, and provides atomic_ref specializations for arithmetic types in many partial specializations.

- libstdc++ provides all atomic_ref specializations in the primary template, while providing atomic full specializations like the standard wording.

The divergence among implementations and standard wording is observable if it's allowed to specialize `atomic` or `atomic_ref`. Also, it's already noted in cplusplus/draft#7556 that it's unimplementable to provide full specializations for all `atomic_ref<pointer-type>`.

On the other hand, it's desired that `_Atomic(T)` behaves as consistently as possible in C and C++ (per 32.5.12 [stdatomic.h.syn]/4). This is possible unachievable if `atomic<T>` is a program-defined specialization.

Given it's unclear whether program-defined specializations of `atomic` and `atomic_ref` make sense, and there're some potential drawbacks allowing them, perhaps it would be better to disallow specializing `atomic` and `atomic_ref`.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

The meaning of "merging specializations" in the note is unclear. It is unclear if well-formed program can differentiate between atomic instantiations produced from the primary template or specializations (full or partial).

Proposed resolution:

This wording is relative to N5032.

1. Modify 32.5.1 [atomics.general] as indicated:

   > -1- Subclause 32.5 [atomics] describes components for fine-grained atomic access. This access is provided via operations on atomic objects.

   > -?- [Note ?: Although some specializations of `atomic` and `atomic_ref` are specified as explicit or partial specializations, the implementation can merge these specializations in an unspecified way, because a program-defined specialization is invalid. — end note]

2. Modify 32.5.7.1 [atomics.ref.generic.general] as indicated:

   > -5- The program is ill-formed if `is_always_lock_free` is `false` and `is_volatile_v<T>` is `true`.

   > -?- The program is ill-formed, no diagnostic required, if it declares an explicit or partial specialization of `atomic_ref`.

3. Modify 32.5.8.1 [atomics.types.generic.general] as indicated:

   > -1- The template argument for `T` shall meet the Cpp17CopyConstructible and Cpp17CopyAssignable requirements. The program is ill-formed if any of

   > […]

   > is `false`.

   > -?- The program is ill-formed, no diagnostic required, if it declares an explicit or partial specialization of `atomic`.

### 4572(i). Key ordering requirements of associative containers are poorly specified

Section: 23.2.7.1 [associative.reqmts.general], 23.4.3.4 [map.modifiers], 23.4.6.4 [set.modifiers] Status: LEWG Submitter: Joaquin M López Muñoz Opened: 2026-04-19 Last modified: 2026-05-30

Priority: 2

View other active issues in [associative.reqmts.general].

View all other issues in [associative.reqmts.general].

View all issues with LEWG status.

Discussion:

23.2.7.1 [associative.reqmts.general] mandates that `Compare` induce a strict weak ordering (SWO) on elements of `Key`. A strict reading of this requirement without further qualifications implies, for instance, that using a container such as `std::set<double>` is UB regardless of what elements go into it (because `std::less<double>` is not a SWO on all values of `double` due to NaN). Assuming that the intent of the standard is to allow for the use of `std::set<double>` and other containers where `Compare` does not induce a full SWO on all possible values of `Key`, there are (at least) two possible relaxations of the original requirement:

1. Let `K` be the set of keys in an associative container `a` at a given point in time: `Compare` induces a SWO on `K` and also on {`k`} ∪ `K` for any `k` involved in an attempted insertion into `a` (either successful or not).

2. `Compare` induces a SWO on `K` and also on {`k`} ∪ `K` for any `k` involved in an attempted insertion (either successful or not) or a lookup operation.

Now, consider the following code:

>     // (A)
>     std::set<double> s = {0.0, 1.0, 2.0, 3.0};
>     auto nan = std::numeric_limits<double>::quiet_NaN();
>     return s.upper_bound(nan) == s.end();

Under interpretation 1, (A) is correct and returns `true`, whereas under interpretation 2 the code is UB because `std::less<double>` does not induce a SWO on `{0.0, 1.0, 2.0, 3.0, NaN}`.

Currently, libstdc++, Microsoft's C++ Standard Library, and libc++ v21 or lower behave as if interpretation 1 is in effect. Starting with version 22, libc++ introduces optimizations that implicitly assume interpretation 2, and the code in fact returns `false`. See this godbolt link, where the different behaviors are shown side by side.

Note, however, that the following pieces of code are not UB:

>     // (B) Global binary search functions
>     std::vector<double> v = {0.0, 3.0, 2.0, 1.0};
>     std::sort(v.begin(), v.end());
>     return std::upper_bound(v.begin(), v.end(), nan) == v.end(); // returns true

>     // (C) Heterogeneous lookup
>     std::set<double, std::less<>> s = {0.0, 1.0, 2.0, 3.0};
>     return s.upper_bound(std::cref(nan)) == s.end(); // returns true

because `upper_bound` in (B) and (C) only requires that the passed key partition the range where lookup happens, and ¬( · < NaN) partitions the range `R` = [0.0, 1.0, 2.0, 3.0] into `R` and [] (see upper.bound and 23.2.7.1 [associative.reqmts.general]/167. The fact that (C) is well defined is an argument against interpretation 2, given that (C) can be seen as a mere syntactic variation of (A).

The status quo (`Compare` must induce a full SWO on all possible values of `Key`) is unacceptably strict and hardly the intent of the standard. As reasonable interpretation is left open, we're beginning to see observable divergences between C++ standard library implementations.

Arguments in favor of partition-based lookup semantics

We posit that interpretation 1, supplemented with partition-based requirements for non-heterogeneous lookup:

Let `K` be the set of keys in an associative container `a` at a given point in time: `Compare` induces a SWO on `K` and also on {`k`} ∪ `K` for any `k` involved in an attempted insertion into `a` (either successful or not). Non-heterogeneous lookup for `k` only requires that `k` partition the range [`a.begin()`, `a.end()`).

is the most useful and reasonable interpretation. Some arguments in favor of this thesis:

- Partition-based lookup semantics is maximally aligned and consistent with current requirements for global lookup algorithms and associative container heterogeneous lookup. In particular:

  - It makes (A) and (C) equivalent, which is a reasonable assumption any non-expert user could make.

  - It makes flat container adaptors truly equivalent to the manually-sorted vectors they are meant to replace:

    >     // (D)
    >     std::vector<double> v = {3.0, 1.0, 0.0, 2.0};
    >     std::sort(v.begin(), v.end());
    >     auto nan = std::numeric_limits<double>::quiet_NaN();
    >     return std::upper_bound(v.begin(), v.end(), nan) == v.end();

    >     // (E) only well defined and equivalent to (D) if partition-based lookup is adopted
    >     std::flat_set<double> s = {3.0, 1.0, 0.0, 2.0};
    >     auto nan = std::numeric_limits<double>::quiet_NaN();
    >     return s.upper_bound(nan) == s.end();

- In the N1313 foundational paper where partition-based semantics was first introduced, David Abrahams states that "[s]trict weak ordering is a great concept for sorting, but maybe it's not appropriate for searching".

  N1313 informed the resolution of LWG 270(i) ("Binary search requirements overly strict"), whose rationale states that "[t]he proposed resolution reinterprets binary search. Instead of thinking about searching for a value in a sorted range, we view that as an important special case of a more general algorithm: searching for the partition point in a partitioned range".

- In N2882 and N3465, early versions of the paper upon which heterogeneous lookup for associative containers was defined (N3657), the wording for non-heterogeneous lookup was indeed partition-based in accordance with Abrahams's conceptual setup (though this exact wording didn't make it into the standard as is, for reasons unknown to the author).

- For a unique associative container `a`, if `Compare` induces a strict partial order on all its possible key values, then partition-based lookup is well defined everywhere. Formally, if `Compare` is a SPO and a set `K` of keys is totally ordered wrt `Compare` (i.e., `K` is a chain, which the range [`a.begin()`, `a.end()`) always satisfies), then `K` is partitioned by any arbitrary `k` (proof trivial).

- Beyond admittedly corner cases such as that of NaN, there are partial orders that directly benefit from partition-based semantics. For instance, if we have a set `s` of non-overlapping interval objects ordered by their natural interval order (such as supported by Boost.ICL, then `s.equal_range(i)` simply returns the range of intervals in `s` that are contained in `i`. Boost.ICL, in fact, implicitly relies on the interpretation of lookup semantics we're proposing here.

On a related note, it may be worth mentioning that the standard text has some general formulation problems regarding the extent of applicability of semantic requirements (both for C++20 `concept`s and pre-C++20 well-defined expression tables, collectively called named requirements). 16.3.2.3 [structure.requirements]/8 tries to provide some leeway for the specific case of `concept`s in a manner described by P2027R0, section "This is a special case of a much bigger problem", as "little more than handwaving".

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

Extensive reflector discussion. Strong opposition to the changes, being a pessimization for all unique-key lookups, implementation divergence, and recent changes in implementtion that silently broke user libraries.

[2026-05-29 LWG telecon; status: New → LEWG]

Proposed resolution:

This wording is relative to N5032.

1. Modify 23.2.7.1 [associative.reqmts.general] as indicated:

   > -2- Each associative container is parameterized on `Key` and an ordering relation `Compare` that induces a strict weak ordering (26.8 [alg.sorting]) on elements of `Key` the values of `Key` in the container at any point in time. In addition, `map` and `multimap` associate an arbitrary mapped type `T` with the `Key`. The object of type `Compare` is called the comparison object of a container.

   > […]

   > -7- In this subclause,

   > 1. (7.1) — `X` denotes an associative container class,

   > 2. […]

   > 3. (7.7) — `a_eq` denotes a value of type `X` when `X` supports equivalent keys,

   > 4. (7.8) — `a_tran` denotes a value of type `X` or `const X` when the qualified-id `X::key_compare::is_transparent` is valid and denotes a type (13.10.3 [temp.deduct]),

   > 5. (7.9) — `i` and `j` meet the Cpp17InputIterator requirements and refer to elements implicitly convertible to `value_type`,

   > 6. […]

   > 7. (7.17) — `t` denotes a value of type `X::value_type`, and

   > 8. (7.18) — `k` denotes a value of type `X::key_type`, and

   > 9. (7.19) — `c` denotes a value of type `X::key_compare` or `const X::key_compare`;

   > 10. […]

   > 11. (7.23) — `kx` is a value such that

   >     1. (7.23.1) — `a` is partitioned with respect to `c(x, kx)` and `!c(kx, x)`, with `c(x, kx)` implying `!c(kx, x)` and with `x` the key value of `e` and `e` in `a`, and

   >     2. (7.23.2) — `kx` is not convertible to either `iterator` or `const_iterator`; and

   >     3. (7.23.?) — if qualified-id `X::key_compare::is_transparent` is not valid or does not denote a type (13.10.3 [temp.deduct]), then `kl`, `ku`, `ke` and `kx` are of type `X::key_type`;

   > 12. […]

   > […]

   > […]

   >     a_uniq.emplace(args)

   > > -47- Result: `pair<iterator, bool>`

   > > -48- Preconditions: `value_type` is Cpp17EmplaceConstructible into `X` from `args`. If `t` is a `value_type` object constructed with `std::forward<Args>(args)...`, `Compare` induces a strict weak ordering on the set of values comprising the key of `t` and all the keys in `a_uniq`.

   > > -49- Effects: Inserts a `value_type` object `t` constructed with `std::forward<Args>(args)...` if and only if there is no element in the container with key equivalent to the key of `t`.

   > > -50- Returns: The `bool` component of the returned pair is `true` if and only if the insertion takes place, and the iterator component of the pair points to the element with key equivalent to the key of `t`.

   > > -51- Complexity: Logarithmic.

   >     a_eq.emplace(args)

   > > -52- Result: `iterator`

   > > -53- Preconditions: `value_type` is Cpp17EmplaceConstructible into `X` from `args`. If `t` is a `value_type` object constructed with `std::forward<Args>(args)...`, `Compare` induces a strict weak ordering on the set of values comprising the key of `t` and all the keys in `a_eq`.

   > > -54- Effects: Inserts a `value_type` object `t` constructed with `std::forward<Args>(args)...`.If a range containing elements equivalent to `t` exists in `a_eq`, `t` is inserted at the end of that range.

   > > -55- Returns: An iterator pointing to the newly inserted element.

   > > -56- Complexity: Logarithmic.

   > […]

   >     a_uniq.insert(t)

   > > -61- Result: `pair<iterator, bool>`

   > > -62- Preconditions: If `t` is a non-const rvalue, `value_type` is Cpp17MoveInsertable into `X`; otherwise, `value_type` is Cpp17CopyInsertable into `X`. `Compare` induces a strict weak ordering on the set of values comprising the key of `t` and all the keys in `a_uniq`.

   > > -63- Effects: Inserts `t` if and only if there is no element in the container with key equivalent to the key of `t`.

   > > -64- Returns: The `bool` component of the returned pair is `true` if and only if the insertion takes place, and the iterator component of the pair points to the element with key equivalent to the key of `t`.

   > > -65- Complexity: Logarithmic.

   >     a_eq.insert(t)

   > > -66- Result: `iterator`

   > > -67- Preconditions: If `t` is a non-const rvalue, `value_type` is Cpp17MoveInsertable into `X`; otherwise, `value_type` is Cpp17CopyInsertable into `X`. `Compare` induces a strict weak ordering on the set of values comprising the key of `t` and all the keys in `a_eq`.

   > > -68- Effects: Inserts `t` and returns the iterator pointing to the newly inserted element. If a range containing elements equivalent to `t` exists in `a_eq`, `t` is inserted at the end of that range.

   > > -69- Complexity: Logarithmic.

   >     a.insert(p, t)

   > > -70- Result: `iterator`

   > > -71- Preconditions: If `t` is a non-const rvalue, `value_type` is Cpp17MoveInsertable into `X`; otherwise, `value_type` is Cpp17CopyInsertable into `X`. `Compare` induces a strict weak ordering on the set of values comprising the key of `t` and all the keys in `a`.

   > > -72- Effects: Inserts `t` if and only if there is no element with key equivalent to the key of `t` in containers with unique keys; always inserts `t` in containers with equivalent keys. `t` is inserted as close as possible to the position just prior to `p`.

   > > -73- Returns: An iterator pointing to the element with key equivalent to the key of `t`.

   > > -74- Complexity: Logarithmic in general, but amortized constant if `t` is inserted right before `p`.

   >     a.insert(i, j)

   > > -75- Result: `void`

   > > -76- Preconditions: `value_type` is Cpp17EmplaceConstructible into `X` from `*i`. Neither `i` nor `j` are iterators into `a`. `Compare` induces a strict weak ordering on the set of values comprising all the keys in `[i, j)` and all the keys in `a`.

   > > -77- Effects: Inserts each element from the range `[i, j)` if and only if there is no element with key equivalent to the key of that element in containers with unique keys; always inserts that element in containers with equivalent keys.

   > > -78- Complexity: `N` log(`a.size() + N`), where `N` has the value `distance(i, j)`.

   >     a.insert_range(rg)

   > > -79- Result: `void`

   > > -80- Preconditions: `value_type` is Cpp17EmplaceConstructible into `X` from `*ranges::begin(rg)`. `rg` and `a` do not overlap. `Compare` induces a strict weak ordering on the set of values comprising all the keys in `rg` and all the keys in `a`.

   > > -81- Effects: Inserts each element from `rg` if and only if there is no element with key equivalent to the key of that element in containers with unique keys; always inserts that element in containers with equivalent keys.

   > > -82- Complexity: `N` log(`a.size() + N`), where `N` has the value `ranges::distance(rg)`.

   > […]

   >     a_uniq.insert(nh)

   > > -84- Result: `insert_return_type`

   > > -85- Preconditions: `nh` is empty or `a_uniq.get_allocator() == nh.get_allocator()` is `true`. If `nh` is not empty, `Compare` induces a strict weak ordering on the set of values comprising the key of `nh` and all the keys in `a_uniq`.

   > > -86- Effects: If `nh` is empty, has no effect. Otherwise, inserts the element owned by `nh` if and only if there is no element in the container with a key equivalent to `nh.key()`.

   > > -87- Returns: If `nh` is empty, inserted is `false`, `position` is `end()`, and `node` is empty. Otherwise if the insertion took place, `inserted` is `true`, `position` points to the inserted element, and `node` is empty; if the insertion failed, `inserted` is `false`, `node` has the previous value of `nh`, and `position` points to an element with a key equivalent to `nh.key()`.

   > > -88- Complexity: Logarithmic.

   >     a_eq.insert(nh)

   > > -89- Result: `iterator`

   > > -90- Preconditions: `nh` is empty or `a_eq.get_allocator() == nh.get_allocator()` is `true`. If `nh` is not empty, `Compare` induces a strict weak ordering on the set of values comprising the key of `nh` and all the keys in `a_eq`.

   > > -91- Effects: If `nh` is empty, has no effect and returns `a_eq.end()`. Otherwise, inserts the element owned by `nh` and returns an iterator pointing to the newly inserted element. If a range containing elements with keys equivalent to `nh.key()` exists in `a_eq`, the element is inserted at the end of that range.

   > > -92- Postconditions: `nh` is empty.

   > > -93- Complexity: Logarithmic.

   >     a.insert(p, nh)

   > > -94- Result: `iterator`

   > > -95- Preconditions: `nh` is empty or `a.get_allocator() == nh.get_allocator()` is `true`. If `nh` is not empty, `Compare` induces a strict weak ordering on the set of values comprising the key of `nh` and all the keys in `a`.

   > > -96- Effects: If `nh` is empty, has no effect and returns `a.end()`. Otherwise, inserts the element owned by `nh` if and only if there is no element with key equivalent to `nh.key()` in containers with unique keys; always inserts the element owned by `nh` in containers with equivalent keys. The element is inserted as close as possible to the position just prior to `p`.

   > > -97- Postconditions: `nh` is empty if insertion succeeds, unchanged if insertion fails.

   > > -98- Returns: An iterator pointing to the element with key equivalent to `nh.key()`.

   > > -99- Complexity: Logarithmic.

   > […]

   >     a.extract(k)

   > > -100- Result: `node_type`

   > > -101- Effects: Removes the first element in the container with key equivalent to `k`.

   > > -102- Returns: A `node_type` owning the element if found, otherwise an empty `node_type`.

   > > -103- Complexity: log(`a.size()`)

   >     a_trana.extract(kx)

   > > -104- Result: `node_type`

   > > -105- Effects: Removes the first element in the container with key `r` such that `!c(r, kx) && !c(kx, r)` is `true`.

   > > -106- Returns: A `node_type` owning the element if found, otherwise an empty `node_type`.

   > > -107- Complexity: log(`a_trana.size()`)

   > […]

   >     a.merge(a2)

   > > -112- Result: `void`

   > > -113- Preconditions: `a.get_allocator() == a2.get_allocator()` is `true`. `Compare` induces a strict weak ordering on the set of values comprising all the keys in `a` and all the keys in `a2`.

   > > -114- Effects: Attempts to extract each element in `a2` and insert it into `a` using the comparison object of `a`. In containers with unique keys, if there is an element in a with key equivalent to the key of an element from `a2`, then that element is not extracted from `a2`.

   > > -115- Postconditions: Pointers and references to the transferred elements of `a2` refer to those same elements but as members of `a`. If `a.begin()` and `a2.begin()` have the same type, iterators referring to the transferred elements will continue to refer to their elements, but they now behave as iterators into `a`, not into `a2`.

   > > -116- Throws: Nothing unless the comparison object throws.

   > > -117- Complexity: `N` log(`a.size()` + `N`), where `N` has the value `a2.size()`.

   >     a.erase(k)

   > > -118- Result: `size_type`

   > > -119- Effects: Erases all elements in the container with key equivalent to `k`.

   > > -120- Returns: The number of erased elements.

   > > -121- Complexity: log(`a.size()`) + `a.count(k)`

   >     a_trana.erase(kx)

   > > -122- Result: `size_type`

   > > -123- Effects: Erases all elements in the container with key `r` such that `!c(r, kx) && !c(kx, r)` is `true`.

   > > -124- Returns: The number of erased elements.

   > > -125- Complexity: log(`a_trana.size()`) + `a_trana.count(kx)`

   > […]

   >     b.find(k)

   > > -141- Result: `iterator`; `const_iterator` for constant `b`.

   > > -142- Returns: An iterator pointing to an element with the key equivalent to `k`, or `b.end()` if such an element is not found.

   > > -143- Complexity: Logarithmic.

   >     a_tranb.find(ke)

   > > -144- Result: `iterator`; `const_iterator` for constant `a_tranb`.

   > > -145- Returns: An iterator pointing to an element with key `r` such that `!c(r, ke) && !c(ke, r)` is `true`, or `a_tranb.end()` if such an element is not found.

   > > -146- Complexity: Logarithmic.

   >     b.count(k)

   > > -147- Result: `size_type`

   > > -148- Returns: The number of elements with key equivalent to `k`.

   > > -149- Complexity: log(`b.size()`) + `b.count(k)`

   >     a_tranb.count(ke)

   > > -150- Result: `size_type`

   > > -151- Returns: The number of elements with key `r` such that `!c(r, ke) && !c(ke, r)`.

   > > -152- Complexity: log(`a_tranb.size()`) + `a_tranb.count(ke)`

   >     b.contains(k)

   > > -153- Result: `bool`

   > > -154- Effects: Equivalent to: `return b.find(k) != b.end();`

   >     a_tranb.contains(ke)

   > > -155- Result: `bool`

   > > -156- Effects: Equivalent to: `return a_tranb.find(ke) != a_tranb.end();`

   >     b.lower_bound(k)

   > > -157- Result: `iterator`; `const_iterator` for constant `b`.

   > > -158- Returns: An iterator pointing to the first element with key not less than `k`, or `b.end()` if such an element is not found.

   > > -159- Complexity: Logarithmic.

   >     a_tranb.lower_bound(kl)

   > > -160- Result: `iterator`; `const_iterator` for constant `a_tranb`.

   > > -161- Returns: An iterator pointing to the first element with key `r` such that !c(r, kl), or `a_tranb.end()` if such an element is not found.

   > > -162- Complexity: Logarithmic.

   >     b.upper_bound(k)

   > > -163- Result: `iterator`; `const_iterator` for constant `b`.

   > > -164- Returns: An iterator pointing to the first element with key greater than `k`, or `b.end()` if such an element is not found.

   > > -165- Complexity: Logarithmic.

   >     a_tranb.upper_bound(ku)

   > > -166- Result: `iterator`; `const_iterator` for constant `a_tranb`.

   > > -167- Returns: An iterator pointing to the first element with key `r` such that `c(ku, r)`, or `a_tranb.end()` if such an element is not found.

   > > -168- Complexity: Logarithmic.

   >     b.equal_range(k)

   > > -169- Result: `pair<iterator, iterator>`; `pair<const_iterator, const_iterator>` for constant `b`.

   > > -170- Effects: Equivalent to: `return make_pair(b.lower_bound(k), b.upper_bound(k));`

   > > -171- Complexity: Logarithmic.

   >     a_tranb.equal_range(ke)

   > > -172- Result: `pair<iterator, iterator>`; `pair<const_iterator, const_iterator>` for constant `a_tranb`.

   > > -173- Effects: Equivalent to: `return make_pair(a_tranb.lower_bound(ke), a_tranb.upper_bound(ke));`

   > > -174- Complexity: Logarithmic.

2. Modify 23.2.7.2 [associative.reqmts.except] as indicated:

   > -1- For associative containers, no `clear()` function throws an exception. `erase(kkx)` does not throw an exception unless that exception is thrown by the container's `Compare` object (if any).

3. Modify 23.4.3.4 [map.modifiers] as indicated:

   >     template<class... Args>
   >       constexpr pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
   >     template<class... Args>
   >       constexpr iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);

   > > -3- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `piecewise_construct`, `forward_as_tuple(k)`, `forward_as_tuple(std::forward<Args>(args)...)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -4- Effects: If the map already contains an element whose key is equivalent to `k`, there is no effect. Otherwise inserts an object of type `value_type` constructed with `piecewise_construct`, `forward_as_tuple(k)`, `forward_as_tuple(std::forward<Args>(args)...)`.

   > > -5- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -6- Complexity: The same as `emplace` and `emplace_hint`, respectively.

   >     template<class... Args>
   >       constexpr pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
   >     template<class... Args>
   >       constexpr iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);

   > > -7- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `piecewise_construct`, `forward_as_tuple(std::move(k))`, `forward_as_tuple(std::forward<Args>(args)...)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -8- Effects: If the map already contains an element whose key is equivalent to `k`, there is no effect. Otherwise inserts an object of type `value_type` constructed with `piecewise_construct`, `forward_as_tuple(std::move(k))`, `forward_as_tuple(std::forward<Args>(args)...)`.

   > > -9- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -10- Complexity: The same as `emplace` and `emplace_hint`, respectively.

   >     template<class K, class... Args>
   >       constexpr pair<iterator, bool> try_emplace(K&& k, Args&&... args);
   >     template<class K, class... Args>
   >       constexpr iterator try_emplace(const_iterator hint, K&& k, Args&&... args);

   > > -11- Constraints: The qualified-id `Compare::is_transparent` is valid and denotes a type. For the first overload, `is_convertible_v<K&&, const_iterator>` and `is_convertible_v<K&&, iterator>` are both `false`.

   > > -12- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `piecewise_construct`, `forward_as_tuple(std::forward<K>(k))`, `forward_as_tuple(std::forward<Args>(args)...)`. `Compare` induces a strict weak ordering on the set of values comprising `key_type(std::forward<K>(k))` and all the keys in `*this`.

   > > -13- Effects: If the map already contains an element whose key is equivalent to `k`, there is no effect. Otherwise, let `r` be `equal_range(k)`. Constructs an object `u` of type `value_type` with `piecewise_construct`, `forward_as_tuple(std::forward<K>(k))`, `forward_as_tuple(std::forward<Args>(args)...)`. If `equal_range(u.first) == r` is `false`, the behavior is undefined. Inserts `u` into `*this`.

   > > -14- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -15- Complexity: The same as `emplace` and `emplace_hint`, respectively.

   >     template<class M>
   >       constexpr pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
   >     template<class M>
   >       constexpr iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);

   > > -16- Mandates: `is_assignable_v<mapped_type&, M&&>` is `true`.

   > > -17- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `k`, `std::forward<M>(obj)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -18- Effects: If the map already contains an element `e` whose key is equivalent to `k`, assigns `std::forward<M>(obj)` to `e.second`. Otherwise inserts an object of type `value_type` constructed with `k`, `std::forward<M>(obj)`.

   > > -19- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -20- Complexity: The same as `emplace` and `emplace_hint`, respectively.

   >     template<class M>
   >       constexpr pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
   >     template<class M>
   >       constexpr iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);

   > > -21- Mandates: `is_assignable_v<mapped_type&, M&&>` is `true`.

   > > -22- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `std::move(k)`, `std::forward<M>(obj)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -23- Effects: If the map already contains an element `e` whose key is equivalent to `k`, assigns `std::forward<M>(obj)` to `e.second`. Otherwise inserts an object of type `value_type` constructed with `std::move(k)`, `std::forward<M>(obj)`.

   > > -24- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -25- Complexity: The same as `emplace` and `emplace_hint`, respectively.

   > > -26- Constraints: `is_assignable_v<mapped_type&, M&&>` is `true`.

   > > -27- Mandates: `is_assignable_v<mapped_type&, M&&>` is `true`.

   > > -28- Preconditions: `value_type` is Cpp17EmplaceConstructible into `map` from `std::forward<K>(k)`, `std::forward<M>(obj)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -29- Effects: If the map already contains an element `e` whose key is equivalent to `k`, assigns `std::forward<M>(obj)` to `e.second`. Otherwise, let `r` be `equal_range(k)`. Constructs an object `u` of type `value_type` with `std::forward<K>(k)`, `std::forward<M>(obj)`. If `equal_range(u.first) == r` is `false`, the behavior is undefined. Inserts `u` into `*this`.

   > > -30- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to `k`.

   > > -31- Complexity: The same as `emplace` and `emplace_hint`, respectively.

4. Modify 23.4.6.4 [set.modifiers] as indicated:

   >     template<class K> constexpr pair<iterator, bool> insert(K&& x);
   >     template<class K> constexpr iterator insert(const_iterator hint, K&& x);

   > > -1- Constraints: The qualified-id `Compare::is_transparent` is valid and denotes a type. For the second overload, `is_convertible_v<K&&, const_iterator>` and `is_convertible_v<K&&, iterator>` are both `false`.

   > > -2- Preconditions: `value_type` is Cpp17EmplaceConstructible into `set` from `std::forward<K>(x)`. `Compare` induces a strict weak ordering on the set of values comprising `k` and all the keys in `*this`.

   > > -3- Effects: If the set already contains an element that is equivalent to `x`, there is no effect. Otherwise, let `r` be `equal_range(x)`. Constructs an object `u` of type `value_type` with `std::forward<K>(x)`. If `equal_range(u) == r` is `false`, the behavior is undefined. Inserts `u` into `*this`.

   > > -4- Returns: In the first overload, the `bool` component of the returned pair is `true` if and only if the insertion took place. The returned iterator points to the set element that is equivalent to `x`.

   > > -5- Complexity: Logarithmic.

### 4573(i). Improve `view_interface::cbegin`

Section: 25.5.3 [view.interface] Status: New Submitter: Hewill Kang Opened: 2026-04-19 Last modified: 2026-05-29

Priority: 3

View other active issues in [view.interface].

View all other issues in [view.interface].

View all issues with New status.

Discussion:

The `view_interface` can provide only a const `cbegin` member when the derived class is `simple-view`, which helps reduce template instantiation.

[2026-05-29; Reflector poll.]

Set priority to 3 after reflector poll.

This does not seem to eliminate class template instantiations, so may not be an improvement. Would benefit from benchmarks.

We could go even further and replace `!simple_range<R>`, with `!input_range<const R>`, due to `ranges::cbegin` and `ranges::cend` using `possibly-constant-range`.

Proposed resolution:

This wording is relative to N5032.

1. Modify 25.5.3 [view.interface] as indicated:

   >     namespace std::ranges {
   >       template<class D>
   >         requires is_class_v<D> && same_as<D, remove_cv_t<D>>
   >       class view_interface {
   >         […]
   >       public:
   >         […]
   >         constexpr auto cbegin() requires input_range<D> && (!simple-view<D>) {
   >           return ranges::cbegin(derived());
   >         }
   >         constexpr auto cbegin() const requires input_range<const D> {
   >           return ranges::cbegin(derived());
   >         }
   >         constexpr auto cend() requires input_range<D> && (!simple-view<D>) {
   >           return ranges::cend(derived());
   >         }
   >         constexpr auto cend() const requires input_range<const D> {
   >           return ranges::cend(derived());
   >         }
   >         […]
   >       };
   >     }

### 4574(i). `constant_wrapper<nullptr, int()>` bypasses `function_ref` class invariant

Section: 22.2.10 [const.wrap.class], 22.10.17.6 [func.wrap.ref] Status: New Submitter: Zhihao Yuan Opened: 2026-04-27 Last modified: 2026-05-29

Priority: 2

View all other issues in [const.wrap.class].

View all issues with New status.

Discussion:

The wording in neither 22.2.10 [const.wrap.class] nor 22.10.17.6 [func.wrap.ref] places a constraint on the 2nd template argument of `constant_wrapper`, so it can be anything. An interpretation of the standard text based on that can cause

>     function_ref fr(constant_wrapper<nullptr, int()>{});

to initialize an object of `function_ref` with invalid state because `int()` isn't a pointer or member pointer.

The example doesn't compile in libstdc++, but not due to the same reason of the following

>     function_ref fr(cw<(int (*)())0>);

From the error messages you can tell that the 1st example fails only at a cautious initialization placed by libstdc++, the 2nd example fails only at `static_assert`, and the following example

>     function_ref fr(constant_wrapper<nullptr, int (*)()>{});

fails at both sites, which means the 1st example bypassed the Mandates clause of the ctor.

Godbolt demo

The 2nd template argument of `constant_wrapper` is defaulted to `decltype(X)::type`, it may need to be required to be `decltype(X)::type` to reliably provide value.

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

This should be resolved by P4206R0.

Proposed resolution:

### 4575(i). No formal wording associates single total order of allocation/deallocation operations with the coherence rule

Section: 17.6.3.5 [new.delete.dataraces] Status: SG1 Submitter: jim x Opened: 2026-05-03 Last modified: 2026-05-29

Priority: 4

View other active issues in [new.delete.dataraces].

View all other issues in [new.delete.dataraces].

View all issues with SG1 status.

Discussion:

17.6.3.5 [new.delete.dataraces] p1 says:

> Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order.

Consider this example:

>     #include <thread>
>     #include <atomic>

>     int main(){
>        std::atomic<bool> flag = false;
>        std::jthread t1([&](){
>           auto p = operator new(sizeof(int));  // #1
>           while(!flag.load(std::memory_order::acquire))
>             ;
>           operator delete(p, sizeof(int)); // #2
>        });

>       std::jthread t2([&](){
>         auto p2 = operator new(sizeof(int));  // #3
>         flag.store(true, std::memory_order::release);
>       });
>     }

Both `#1` and `#3` happens-before `#2`. Is it possible that `#1` and `#3` return the same address? We don't associate the single total order of allocation/deallocation operations with the happens-before(i.e., coherence rule defined in 6.10.2.2 [intro.races]).

Suggested Resolution Approach:

Associate the single total order with the coherence rule; this issue is similar to LWG 4519(i). The current wording is hard to use proof by contradiction to prove that `#1` and `#3` cannot return the same address. Moreover, "the next allocation (if any) in this order" is also vague, which the "next" is relative to? Is the next allocation relative to the previous allocation in the total order, or to the previous deallocation? However, in this example, even if we associate the coherence rule with happens-before, the total order is `#1 < #3 < #2`(or, `#3 < #1 < #2`), it seems that the "next allocation" is meaningless if it is relative to the deallocation. Furthermore, de-shall is meaningful here because we impose a requirement on the implementations.

[2026-05-29; Reflector poll.]

Set priority to 4 and send to SG1 after reflector poll.

Proposed resolution:

### 4576(i). `{can_}substitute` specification is (still) ill-formed

Section: 21.4.15 [meta.reflection.substitute] Status: New Submitter: S. B. Tam Opened: 2026-05-05 Last modified: 2026-05-29

Priority: 2

View all other issues in [meta.reflection.substitute].

View all issues with New status.

Discussion:

The use of `template [: x :]` (introduced in LWG 4316(i)) is incorrect: `template [: x :]` is a splice-expression (which can only designate a function template or variable template), and an unparenthesized splice-expression is not allowed as a template-argument per 13.3 [temp.names]/6.

[2026-05-29; Reflector poll.]

Set priority to 2 after reflector poll.

We should merge 1.1 and 1.2.

Proposed resolution:

This wording is relative to N5032.

1. Modify 21.4.15 [meta.reflection.substitute] as indicated:

   > -1- Let `TARG-SPLICE(x)` be:

   > 1. (1.1) — `template [: x :]`a template-argument that denotes what `x` represents if `is_template(x)` is `true`, otherwise

   > 2. (1.2) — `typename [: x :]` if `is_type(x)` is `true`, otherwise

   > 3. (1.3) — `([: x :])`.

### 4577(i). Hardening `view_interface::operator[]`

Section: 25.5.3 [view.interface] Status: Ready Submitter: Hewill Kang Opened: 2026-05-11 Last modified: 2026-06-09

Priority: Not Prioritized

View other active issues in [view.interface].

View all other issues in [view.interface].

View all issues with Ready status.

Discussion:

Hardening `view_interface::operator[]` to prevent out-of-bounds access is worthwhile for the `sized_range` cases, and we have already done so for other standard views such as `span` and `string_view`'s `operator[]`.

[2026-05-29 LWG telecon; status: New → Ready]

[2026-06-03; status: Ready → Open]

Reopen to bump the feature test macro.

Previous resolution [SUPERSEDED]

This wording is relative to N5046.

1. Modify 25.5.3 [view.interface], class template `view_interface` synopsis, as indicated:

   >     namespace std::ranges {
   >       template<class D>
   >         requires is_class_v<D> && same_as<D, remove_cv_t<D>>
   >       class view_interface {
   >         […]
   >       public:
   >         […]
   >         template<random_access_range R = D>
   >           constexpr decltype(auto) operator[](range_difference_t<R> n) {
   >             return ranges::begin(derived())[n];
   >           };
   >         template<random_access_range R = const D>
   >           constexpr decltype(auto) operator[](range_difference_t<R> n) const {
   >             return ranges::begin(derived())[n];
   >           };
   >       };
   >     }

2. Modify 25.5.3.2 [view.interface.members] as indicated:

   >     constexpr decltype(auto) back() requires bidirectional_range<D> && common_range<D>;
   >     constexpr decltype(auto) back() const
   >       requires bidirectional_range<const D> && common_range<const D>;

   > > -3- Hardened preconditions: `!empty()` is `true`.

   > > -4- Effects: Equivalent to: `return *ranges::prev(ranges::end(derived()));`

   >     template<random_access_range R = D>
   >       constexpr decltype(auto) operator[](range_difference_t<R> n);
   >     template<random_access_range R = const D>
   >       constexpr decltype(auto) operator[](range_difference_t<R> n) const;

   > > -?- Hardened preconditions: If `D` models `sized_range`, then `n >= 0 && n < ranges::distance(derived())` is `true`.

   > > -?- Effects: Equivalent to: `return ranges::begin(derived())[n];`

[2026-06-03; Jonathan provides new wording]

[Brno 2026-06-08; Change status Open → Ready.]

Proposed resolution:

This wording is relative to N5046.

1. Modify 17.3.2 [version.syn], Header `<version>` synopsis, as indicated:

   >     #define __cpp_lib_hardened_vector 202502L // also in <vector>
   >     #define __cpp_lib_hardened_view_interface 202506YYYYMML // also in <ranges>

2. Modify 25.5.3 [view.interface], class template `view_interface` synopsis, as indicated:

   >     namespace std::ranges {
   >       template<class D>
   >         requires is_class_v<D> && same_as<D, remove_cv_t<D>>
   >       class view_interface {
   >         […]
   >       public:
   >         […]
   >         template<random_access_range R = D>
   >           constexpr decltype(auto) operator[](range_difference_t<R> n) {
   >             return ranges::begin(derived())[n];
   >           };
   >         template<random_access_range R = const D>
   >           constexpr decltype(auto) operator[](range_difference_t<R> n) const {
   >             return ranges::begin(derived())[n];
   >           };
   >       };
   >     }

3. Modify 25.5.3.2 [view.interface.members] as indicated:

   >     constexpr decltype(auto) back() requires bidirectional_range<D> && common_range<D>;
   >     constexpr decltype(auto) back() const
   >       requires bidirectional_range<const D> && common_range<const D>;

   > > -3- Hardened preconditions: `!empty()` is `true`.

   > > -4- Effects: Equivalent to: `return *ranges::prev(ranges::end(derived()));`

   >     template<random_access_range R = D>
   >       constexpr decltype(auto) operator[](range_difference_t<R> n);
   >     template<random_access_range R = const D>
   >       constexpr decltype(auto) operator[](range_difference_t<R> n) const;

   > > -?- Hardened preconditions: If `D` models `sized_range`, then `n >= 0 && n < ranges::distance(derived())` is `true`.

   > > -?- Effects: Equivalent to: `return ranges::begin(derived())[n];`

### 4578(i). `std::start_lifetime(r)` should not be able to poke objects outside the current evaluation

Section: 20.2.6 [obj.lifetime] Status: Core Submitter: Tomasz Kamiński Opened: 2026-05-15 Last modified: 2026-05-22

Priority: Not Prioritized

View other active issues in [obj.lifetime].

View all other issues in [obj.lifetime].

View all issues with Core status.

Discussion:

As currently specified, the following code is well-formed:

>     union U { int x = 10; float f[2]; };
>     U u;

>     consteval {
>       std::start_lifetime(u.f); // changes active member
>     };

>     void foo() {
>       U u;
>       constexpr int x = (std::start_lifetime(u.f), 10);
>     }

The `start_lifetime(r)` when evaluated at compile time, should require that `r` is a subobject of the object created during the enclosing constant evaluation.

Previous resolution [SUPERSEDED]

This wording is relative to N5046.

1. Modify 20.2.6 [obj.lifetime] as indicated:

   >     template<class T>
   >       constexpr void start_lifetime(T& r) noexcept;

   > > -1- Mandates: `T` is a complete type and an implicit-lifetime (6.9 [basic.types]) aggregate (9.5.2 [dcl.init.aggr]).

   > > -?- Constant When: `r` refers to a non-volatile object whose lifetime began within the evaluation of the core constant expression (7.7.2 [expr.const.core]) which includes the invocation of `start_lifetime`, or a sub-object of such an object.

   > > -2- Effects: If the object referenced by `r` is already within its lifetime (6.8.4 [basic.life]), no effects. Otherwise, begins the lifetime of the object referenced by `r`.

[2026-05-22; LWG telecon. Status → Core.]

Corrected example, to make it well-formed.

The recommendation is to include general prohibition in 7.7.2 [expr.const.core], that would prevent us from starting lifetime of object outside current evaluation.

### 4580(i). `is_enumerable_type`: Definition of "enumerable type" looks strange

Section: 21.4.7 [meta.reflection.queries] Status: New Submitter: S. B. Tam Opened: 2026-05-18 Last modified: 2026-05-24

Priority: Not Prioritized

View other active issues in [meta.reflection.queries].

View all other issues in [meta.reflection.queries].

View all issues with New status.

Discussion:

21.4.7 [meta.reflection.queries] p30 says:

> -30- A type `T` is enumerable from a point `P` if either

> 1. (30.1) — `T` is a class type complete at point `P` or

> 2. (30.2) — `T` is an enumeration type defined by a declaration `D` such that `D` is reachable from `P` but `P` does not occur within an enum-specifier of `D` (9.8.1 [dcl.enum]).

The second bullet looks strange to a language lawyer: an enum type can only be declared by opaque-enum-declarations and enum-specifiers (9.8.1 [dcl.enum]), and only the latter are definitions (6.2 [basic.def] p2). That is, `D` must be an enum-specifier. What does it mean to say "an enum-specifier of an enum-specifier"?

I think, instead of saying "an enum-specifier of `D`", the second bullet could simply refer to `D` as an enum-specifier.

Proposed resolution:

This wording is relative to N5046.

1. Modify 21.4.7 [meta.reflection.queries] as indicated:

   >     consteval bool is_enumerable_type(info r);

   > > -30- A type `T` is enumerable from a point `P` if either

   > > 1. (30.1) — `T` is a class type complete at point `P` or

   > > 2. (30.2) — `T` is an enumeration type defined by a declaration an enum-specifier `D` (9.8.1 [dcl.enum]) such that `D` is reachable from `P` but `P` does not occur within an enum-specifier of `D` (9.8.1 [dcl.enum]).

### 4581(i). `constant_of(^^v)` for variable `v` of reference to array/function type produces reflection of pointer constant

Section: 21.4.7 [meta.reflection.queries] Status: New Submitter: S. B. Tam Opened: 2026-05-18 Last modified: 2026-05-24

Priority: Not Prioritized

View other active issues in [meta.reflection.queries].

View all other issues in [meta.reflection.queries].

View all issues with New status.

Discussion:

After CWG 3111, `constant_of` uses `reflect_constant_array` and `reflect_function` for arrays and functions respectively. But since it inspects `type_of(r)` and not `remove_reference(type_of(r))`, variables of reference types will still decay to pointers. It's unclear to me whether this is intended.

Proposed resolution:

### 4582(i). Specializations of `layout_transpose` do not meet the layout mapping policy requirements

Section: 29.9.10.3 [linalg.transp.layout.transpose], 23.7.3.4.3 [mdspan.layout.policy.reqmts] Status: New Submitter: Charles Hussong Opened: 2026-05-19 Last modified: 2026-05-25

Priority: Not Prioritized

View all issues with New status.

Discussion:

29.9.10.3 [linalg.transp.layout.transpose] bullet (3.2) specifies a Mandate that the rank of the extents used with this layout must equal 2. However, the layout mapping policy requirements at 23.7.3.4.3 [mdspan.layout.policy.reqmts] are worded in the following way:

> A type `MP` meets the layout mapping policy requirements if for a type `E` that is a specialization of `extents`, `MP::mapping<E>` is valid and denotes a type `X` that meets the layout mapping requirements (23.7.3.4.2 [mdspan.layout.reqmts]), […]

We interpret this to mean that in order for `MP` to be a layout mapping policy, it must be possible to instantiate `MP::mapping<E>` for any extents `E`, with no additional mandates. Since `layout_transpose` does not satisfy this, specializations of it do not meet the layout mapping policy, which contradicts 29.9.10.1 [linalg.transp.intro] p1, and 29.9.10.3 [linalg.transp.layout.transpose] p1, and makes any `mdspan` using `layout_transpose` (including the return type of `transposed`(29.9.10.4 [linalg.transp.transposed])) ill-formed.

I think the most straightforward solution to this would be to change the layout mapping policy requirements:

> A type `MP` meets the layout mapping policy requirements if for aat least one type `E` that is a specialization of `extents`, `MP::mapping<E>` is valid and denotes a type `X` that meets the layout mapping requirements […]

Unfortunately this is a much looser requirement, but I'm not sure how else to accommodate a sensible definition of `layout_transpose` within the stricter version.

Considered alternatives:

`layout_transpose` could be redefined to either always transpose the first two or last two ranks. I did not submit this because it could easily get confusing for users (was it the first two or the last two, or maybe something else entirely?) and there's no sensible behavior for rank 1.

Another option would be to replace the notion of "meet the layout mapping policy requirements" with "meet the layout mapping policy requirements for extents `E`". My understanding is that this goes against the spirit of the design where the layout mapping policy is not templated on the specific extents, while the layout mapping is.

Proposed resolution:

This wording is relative to N5046.

1. Modify 23.7.3.4.3 [mdspan.layout.policy.reqmts] as indicated:

   > -1- A type `MP` meets the layout mapping policy requirements if for aat least one type `E` that is a specialization of `extents`, `MP::mapping<E>` is valid and denotes a type `X` that meets the layout mapping requirements (23.7.3.4.2 [mdspan.layout.reqmts]), and for which the qualified-id `X::layout_type` is valid and denotes the type `MP` and the qualified-id `X::extents_type` denotes `E`.

### 4584(i). `std::hive::sort` should add Cpp17MoveConstructible to its preconditions on `T`

Section: 23.3.9.5 [hive.operations] Status: New Submitter: Joaquín M López Muñoz Opened: 2026-05-24 Last modified: 2026-05-24

Priority: Not Prioritized

View other active issues in [hive.operations].

View all other issues in [hive.operations].

View all issues with New status.

Discussion:

`std::hive::sort` (23.3.9.5 [hive.operations]) requires that `T` be Cpp17MoveInsertable into `hive`, Cpp17MoveAssignable, and Cpp17Swappable. It does not require `T` to be Cpp17MoveConstructible, which is a prerequisite for the internal use of `std::sort` by the implementation. Note that, in general, Cpp17MoveInsertable into `hive` (or any container) does not imply Cpp17MoveConstructible. Both the de facto reference implementation for `std::hive` (Matthew Bentley's plf::hive) and the ongoing implementation for VS use `std::sort` directly on `T`.

Strictly speaking, the lack of this precondition can be salvaged by invoking `std::sort` on a range of values of type `X`, where `X` is an ad-hoc type wrapping a `T` value and an allocator object which implements move construction in terms of `std::allocator_traits<A>::construct`. So, even if this issue is resolved as NAD, that would be informative for standard library implementors so that they can potentially fix their code.

Proposed resolution:

This wording is relative to N5046.

1. Modify 23.3.9.5 [hive.operations] as indicated:

   >     template<class Compare = less<T>>
   >       void sort(Compare comp = Compare());

   > > -13- Preconditions: `T` is Cpp17MoveInsertable into `hive`, Cpp17MoveConstructible, Cpp17MoveAssignable, and Cpp17Swappable.

   > > -14- Effects: Sorts `*this` according to the `comp` function object. If an exception is thrown, the order of the elements in `*this` is unspecified.

   > > […]

### 4585(i). Applying `std::meta::alignment_of` to a not-yet-defined variable

Section: 21.4.12 [meta.reflection.layout] Status: New Submitter: S. B. Tam Opened: 2026-05-29 Last modified: 2026-05-29

Priority: Not Prioritized

View other active issues in [meta.reflection.layout].

View all other issues in [meta.reflection.layout].

View all issues with New status.

Discussion:

For a reflection value `r` that represents a variable, `std::meta::alignment_of(r)` is specified to return the alignment requirement of the variable. If the variable's definition has an `alignas` specifier, its non-defining declaration does not (as permitted by 9.13.2 [dcl.align] p6), and only the latter is reachable in the evaluation context, is the `std::meta::alignment_of` call valid? If yes, what should it return?

Example:

>     #include <meta>

>     extern char ch;
>     constexpr auto x = std::meta::alignment_of(^^ch);

>     alignas(64) char ch;
>     constexpr auto x2 = x; // x2 is 1 or 64?

Proposed resolution:

### 4586(i). Partial specialization of `std::format_kind` for `std::optional` is not marked hosted

Section: 16.3.3.7 [freestanding.item], 22.5.2 [optional.syn], 28.5.1 [format.syn] Status: New Submitter: Jiang An Opened: 2026-05-29 Last modified: 2026-05-29

Priority: Not Prioritized

View all other issues in [freestanding.item].

View all issues with New status.

Discussion:

Currently, `<optional>` is a mostly freestanding header and provides a partial specialization of `std::format_kind` which isn't mark `// hosted` in the synopsis (22.5.2 [optional.syn]). The current rules in 16.3.3.7 [freestanding.item] p4 seemingly requires the partial specialization to be a freestanding item, while nothing seems to requires the primary template (provided by `<format>`) to be freestanding.

The status quo looks quite weird. Perhaps we should make the freestanding status same for the primary template and the partial specialization.

Proposed resolution:

This wording is relative to N5046.

> [Drafting Note: Three options are prepared, depicted below by Option A, Option B, and Option C, respectively, where option C is mutually exclusive with option A and B.

> Note that libstdc++ effectively implemented option A recently (in commit 23b9ac257cf508bc4fa479c82fcfc768c4abcd79).]

Option A: Just marking the partial specialization hosted

1. Modify 22.5.2 [optional.syn], header `<optional>` synopsis, as indicated:

   >     […]
   >     template<class T>
   >       constexpr bool ranges::enable_view<optional<T>> = true;
   >     template<class T>
   >       constexpr auto format_kind<optional<T>> = range_format::disabled; // hosted
   >     template<class T>
   >       constexpr bool ranges::enable_borrowed_range<optional<T&>> = true;
   >     […]

Option B: Adding a general rule that infers hosted-ness of specializations

1. Modify 16.3.3.7 [freestanding.item] as indicated:

   > -4- A declaration in a synopsis is a freestanding item if […]

   > -5- An entity or deduction guide is a freestanding item if its introducing declaration is not followed by a comment that includes hosted, and is: […]

   > -6- A macro is a freestanding item if it is defined in a header synopsis and […]

   > -?- A partial or full template specialization is not a freestanding item if its primary template is not a freestanding item.

Option C: Making `range_format` and `format_kind` freestanding

1. Modify Table 27 [tab:headers.cpp.fs] as indicated:

   > Table 27: C++ headers for freestanding implementations [tab:headers.cpp.fs]
   > | Subclause | Header
   > --- | --- | ---
   > `[…]`
   > 28.2 [charconv] | Primitive numeric conversions | `<charconv>`
   > 28.5 [format] | Formatting | `<format>`
   > 29.5 [rand] | Random number generation | `<random>`
   > `[…]`

2. Modify 28.5.1 [format.syn], header `<format>` synopsis, as indicated:

   >     […]
   >     // 28.5.7 [format.range], formatting of ranges
   >     // 28.5.7.1 [format.range.fmtkind], variable template format_kind
   >     enum class range_format {
   >       disabled,
   >       map,
   >       set,
   >       sequence,
   >       string,
   >       debug_string
   >     };

   >     template<class R>
   >       constexpr unspecified format_kind = unspecified;         // freestanding

   >     template<ranges::input_range R>
   >       requires same_as<R, remove_cvref_t<R>>
   >       constexpr range_format format_kind<R> = see below;       // freestanding
   >     […]

### 4587(i). Missing complexity for `unordered_map::at`

Section: 23.5.3.3 [unord.map.elem] Status: Ready Submitter: Pablo Halpern Opened: 2026-06-09 Last modified: 2026-06-15

Priority: Not Prioritized

View all other issues in [unord.map.elem].

View all issues with Ready status.

Discussion:

During review of P3091R5 Pablo pointed out that there is no Complexity: element for `unordered_map::at`.

[Brno 2026-06-12; Status changed: New → Ready.]

Proposed resolution:

This wording is relative to N5046.

1. Modify 23.5.3.3 [unord.map.elem] as indicated:

   >     constexpr mapped_type& at(const key_type& k);
   >     constexpr const mapped_type& at(const key_type& k) const;

   > -5- Returns: A reference to `x.second`, where `x is the (unique) element whose key is equivalent to` k`.

   > -6- Throws: An exception object of type `out_of_range` if no such element is present.

   > -?- Complexity: Average case constant, worst case linear in `size()`.

   >     template<class K> constexpr mapped_type&       at(const K& k);
   >     template<class K> constexpr const mapped_type& at(const K& k) const;

   > -7- Constraints: The qualified-ids `Hash::is_transparent` and `Pred::is_transparent` are valid and denote types.

   > -8- Preconditions: The expression `find(k)` is well-formed and has well-defined behavior.

   > -9- Returns: A reference to `find(k)->second`.

   > -10- Throws: An exception object of type `out_of_range` if `find(k) == end()` is `true`.

   > -?- Complexity: Average case constant, worst case linear in `size()`.

### 4588(i). `mdspan` issue due to rewording for canonicalize slice

Section: 23.7.3.7.1 [mdspan.sub.overview] Status: Ready Submitter: Christian Trott Opened: 2026-06-09 Last modified: 2026-06-12

Priority: Not Prioritized

View all issues with Ready status.

Discussion:

There is an oversight in paragraph 8 of 23.7.3.7.1 [mdspan.sub.overview] `dynamic_extent`. This is plainly an oversight and can be corrected by introducing a new 8.1 paragraph:

> — `S` is `E::index_type` or `full_extent_t`, otherwise

If `S` is `index_type` (i.e. a runtime value) no further condition on the validity of the type exists, and if it is `full_extent_t`, it is trivially a valid slice specifier type.

Note that a constant integral argument (`constant_wrapper`) is handled at the end in current paragraph 8.2.

Note furthermore that, `S` is already a slice specifier after canonicalization in this paragraph (or rather in the context where "valid slice specifier type" is used), hence we don't need to consider types convertible to things like `full_extent_t` or `index_type`.

[Brno 2026-06-11; Change status: New → Ready.]

Proposed resolution:

This wording is relative to N5046.

1. Modify 23.7.3.7.1 [mdspan.sub.overview] as indicated:

   > -8- Given a type `E` that is a specialization of `extents`, a type `S` is a valid submdspan slice type for the kth extent of `E` if `S` is a canonical slice type for `E::index_type`, and for `x` equal to `E::static_extent(k)`, either `x` is equal to `dynamic_extent`; or

   > 1. (8.?) — `S` is `E::index_type` or `full_extent_t`, or
   > 2. (8.1) — if `S` is a specialization of `extent_slice`, then […]

### 4589(i). `std::format` should escape null as `\0`

Section: 28.5.6.5 [format.string.escaped] Status: LEWG Submitter: Tomasz Kamiński Opened: 2026-06-10 Last modified: 2026-06-10

Priority: Not Prioritized

View all other issues in [format.string.escaped].

View all issues with LEWG status.

Discussion:

As currently specified the debug output of `std::format` for a string containing a null terminator should escape it as `\u{0}` instead of the more conventional `\0`.

    std::string_view sv("a\0b", 3);
    std::print("{:?}", sv); // "a\u{0}b" not "a\0b"

[Brno 2026-06-10; Status changed: New → LEWG.]

LWG initially approved this as Ready, but Davis pointed out that this would produce strings that resemble an octal literal, e.g. `"abc\0123"`. LEWG should see it.

Proposed resolution:

This wording is relative to N5046.

1. Modify [tab:format.escape.sequences], Table 114 — Mapping of characters to escape sequences:

   > Character | Escape sequence
   > --- | ---
   > U+0000 NULL | \0
   > … | …

### 4590(i). Unclear how `num_get` extracts `void*` values

Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: New Submitter: Jonathan Wakely Opened: 2026-06-10 Last modified: 2026-06-10

Priority: Not Prioritized

View other active issues in [facet.num.get.virtuals].

View all other issues in [facet.num.get.virtuals].

View all issues with New status.

Discussion:

The wording for `std::num_get::do_get` says that we determine a conversion specifier, but then that is never used. Stage 2 says it accumulates characters that are "allowed as the next character of an input field of the conversion specifier" but what says if they're allowed? We never say what the conversion specifier means. In C++03 Stage 3 of processing specified in terms of `scanf`, so the conversion specifier made sense (kind of ... but that wasn't explicit, it was still unspecified and do we mean conversion specification? I don't think `%p` is a conversion specifier, it's a conversion specification with the conversion specifier character `p`).

LWG 23(i) changed the spec to no longer refer to `scanf`, so now the meaning of the conversion specifier is even more unclear, and the manner in which `void*` is extracted is completely undefined. We use one of `strtol`, `strtoll,` strtold` and (since LWG 1169(i)) `strtof`, and `strtod`. But none of those can be used for `void*`.

Even if we say that `void*` still uses `scanf`, the representation of `void*` used for `scanf` in the C standard is also loosely specified. It says "Matches an implementation-defined set of sequences, which should be the same as the set of sequences that may be produced by the `%p` conversion of the `fprintf` function." And `fprintf` says "converted to a sequence of printing characters, in an implementation-defined manner."

Proposed resolution:

### 4591(i). `std::meta::current_function` should probably work in a local class

Section: 21.4.8 [meta.reflection.scope] Status: New Submitter: S. B. Tam Opened: 2026-05-31 Last modified: 2026-06-13

Priority: Not Prioritized

View all issues with New status.

Discussion:

`std::meta::current_function` is specified to throw if `CURRENT-SCOPE(P)` does not represent a function. It is implied that `current_function` throws if the call is directly contained in a local class, in which case it's actually in a function body, but indirectly.

This might be an oversight, as neither P3795 (which introduced this function) nor https://wg21.link/P3795/github has any discussion concerning local classes.

Example:

>     #include <meta>

>     template<auto V> struct A { static constexpr auto value = V; };

>     consteval std::meta::info test() {
>       struct Local { using T = A<std::meta::current_function()>; }; // valid or not?
>       return Local::T::value;
>     }

Proposed resolution:

### 4592(i). Preconditions of some virtual functions `std::codecvt` on character ranges are in a possibly unintended form

Section: 28.3.4.2.5.3 [locale.codecvt.virtuals] Status: New Submitter: Jiang An Opened: 2026-06-01 Last modified: 2026-06-13

Priority: Not Prioritized

View other active issues in [locale.codecvt.virtuals].

View all other issues in [locale.codecvt.virtuals].

View all issues with New status.

Discussion:

Currently, some preconditions in 28.3.4.2.5.3 [locale.codecvt.virtuals] require that "`first <= last` is well-defined and `true`", where `first` and `last` are pointer parameters that are intended to denote a range. These preconditions seem specified too relaxedly, and make some necessary preconditions implicitly specified in Effects. Per 7.6.9 [expr.rel], comparing pointers that are not associated with the same storage instance still has well-defined behavior although the result is unspecified. And even if the result is specified to be true, it's possible for `first` and `last` to point to elements of different array subobjects in the same complete object, which renders `last` not reachable from `first`.

Proposed resolution:

This wording is relative to N5046.

1. Modify 28.3.4.2.5.3 [locale.codecvt.virtuals] as indicated:

   >     result do_out(
   >       stateT& state,
   >       const internT* from, const internT* from_end, const internT*& from_next,
   >       externT* to, externT* to_end, externT*& to_next) const;

   >     result do_in(
   >       stateT& state,
   >       const externT* from, const externT* from_end, const externT*& from_next,
   >       internT* to, internT* to_end, internT*& to_next) const;

   > > -1- Preconditions: `(from <= from_end && to <= to_end)` is well-defined and `true``[from, from_end)` and `[to, to_end)` are both valid ranges; `state` is initialized, if at the beginning of a sequence, or else is equal to the result of converting the preceding characters in the sequence.

   > > […]

   >     result do_unshift(stateT& state, externT* to, externT* to_end, externT*& to_next) const;

   > > -7- Preconditions: `(to <= to_end)` is well-defined and `true``[to, to_end)` is a valid range; `state` is initialized, if at the beginning of a sequence, or else is equal to the result of converting the preceding characters in the sequence.

   > > […]

   > […]

   >     int do_length(stateT& state, const externT* from, const externT* from_end, size_t max) const;

   > > -12- Preconditions: `(from <= from_end)` is well-defined and `true``[from, from_end)` is a valid range; `state` is initialized, if at the beginning of a sequence, or else is equal to the result of converting the preceding characters in the sequence.

   > > […]

### 4593(i). The first template argument of `std::saturating_cast` must be specifiable

Section: 26.10.17.2 [numeric.sat.cast] Status: New Submitter: Jiang An Opened: 2026-06-02 Last modified: 2026-06-13

Priority: Not Prioritized

View all issues with New status.

Discussion:

Currently, 26.2 [algorithms.requirements]/15 allows implementations to reject explicitly specifying template arguments for algorithms. Unfortunately, `std::saturating_cast` is also an algorithm as it is specifying in the "Algorithms library" chapter, and its first template argument must be explicitly specified. Without being "explicitly stated otherwise", `std::saturating_cast` is effectively unusable.

Proposed resolution:

This wording is relative to N5046.

1. Modify 26.10.17.2 [numeric.sat.cast] as indicated:

   >     template<class R, class T>
   >       constexpr R saturating_cast(T x) noexcept;

   > > -1- Constraints: `R` and `T` are signed or unsigned integer types (6.9.2 [basic.fundamental]).

   > > -2- Returns: If `x` is representable as a value of type `R`, `x`; otherwise, either the largest or smallest representable value of type `R`, whichever is closer to the value of `x`.

   > > -?- Remarks: An invocation may explicitly specify an argument for the template parameter `R`.

### 4594(i). `data_member_spec` should throw on anonymous union types

Section: 21.4.17 [meta.reflection.define.aggregate] Status: New Submitter: Jakub Jelinek Opened: 2026-06-04 Last modified: 2026-06-13

Priority: Not Prioritized

View all other issues in [meta.reflection.define.aggregate].

View all issues with New status.

Discussion:

CWG 3130 added to 11.5.2 [class.union.anon]:

> "Each object of such an unnamed type shall be such an unnamed object."

restriction. I think we should therefore throw from `data_member_spec` if one attempts to use it with an anonymous union type.

>     #include <meta>

>     struct S { union { int a; long b; };
>     constexpr auto ctx = std::meta::access_context::unchecked ();
>     using U = [: members_of (^^S, ctx)[0] :];
>     constexpr auto m = data_member_spec (^^U, { .name = "_" });

So, add after 21.4.17 [meta.reflection.define.aggregate] bullet (5.2):

> 1. — `dealias(type)` does not represent an anonymous union type,

Though, maybe better:

> 1. — `remove_all_extents(dealias(type))` does not represent an anonymous union type,

Proposed resolution:

This wording is relative to N5046.

1. Modify 21.4.17 [meta.reflection.define.aggregate] as indicated:

   >     consteval info data_member_spec(info type, data_member_options options);

   > > -4- Returns: […]

   > > -5- Throws: `meta::exception` unless the following conditions are met:

   > > 1. (5.1) — `dealias(type)` represents either an object type or a reference type;

   > > 2. (5.?) — `remove_all_extents(dealias(type))` does not represent an anonymous union type,

   > > 3. (5.2) — […]

   > > 4. […]

### 4595(i). `byte_string` and `wide_string` are not yet zombie names

Section: 16.4.5.3.2 [zombie.names] Status: New Submitter: Jiang An Opened: 2026-06-09 Last modified: 2026-06-14

Priority: Not Prioritized

View all other issues in [zombie.names].

View all issues with New status.

Discussion:

Currently, `byte_string` and `wide_string`, which were member typedef-names of removed `wstring_convert`, are not listed in 16.4.5.3.2 [zombie.names]. In the same concerns as LWG 3885(i), perhaps we should also make them zombie names.

Proposed resolution:

This wording is relative to N5046.

1. Modify 16.4.5.3.2 [zombie.names], Table 39 — "Zombie object-like macros" [tab:zombie.names.objmacro] as indicated:

   > `argument_type`
   > `byte_string`
   > `first_argument_type`
   > `io_state`
   > `op`
   > `open_mode`
   > `preferred`
   > `second_argument_type`
   > `seek_dir`
   > `strict`
   > `wide_string`

### 4596(i). Gratuitous UB via manifestly constant-evaluated contexts in signal handlers

Section: 17.14.5 [support.signal] Status: New Submitter: Hubert Tong Opened: 2026-06-10 Last modified: 2026-06-14

Priority: Not Prioritized

View all other issues in [support.signal].

View all issues with New status.

Discussion:

17.14.5 [support.signal] paragraph 2 specifies that a signal handler invocation has undefined behaviour if it includes an evaluation that is not signal-safe.

Such an included evaluation (consistent with CWG's understanding; see CWG 3162) may occur within a manifestly constant-evaluated context, in which case, the specified undefined behaviour is gratuitous.

Proposed resolution: