You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CWG3123 [stmt.expand] begin(E)/end(E) should perform overload resolution #803
argument-dependent lookups for begin(E) and for end(E) each find at least one function or function template.
This wording seems to imply that if any function or function template with the name begin/end (at least one each) is found via ADL, then E is expansion-iterable. This seems much too broad as it doesn’t take into consideration whether begin(E) or end(E) would even be well-formed.
E.g. if E is of type std::tuple<...>, then ADL for begin/end in namespace std will find quite a few functions (or function templates), but none of them accept a parameter of type std::tuple<...>, and the intent is surely that expansion over a std::tuple<...> be destructuring rather than iterating.
Suggested resolution:
Change the requirement from name lookup succeeding to overload resolution succeeding (notably, if overload resolution succeeds but there is no usable candidate, the program should be ill-formed).
This is not the only example of core language rules that are based on a name being found rather than whether overload resolution succeeds. We also have them in e.g. [expr.await] and [dcl.struct.bind]. I think there must be a reason why CWG likes it this way, even though I don't know what it is.
We usually do that for member functions (I thought), so that we "lock in" to the begin/end interpretation even if, due to slight mishaps in the declaration, the eventual expression involving begin/end would fail to compile. This protects users against accidents.
argument-dependent lookups for begin(E) and for end(E) each find at least one viable function or function template.
Also we're not checking if E.begin() or E.end() are actually viable either, since that part just checks if they exist at all. So maybe something similar for the first bullet?
Also we're not checking if E.begin() or E.end() are actually viable either,
That's generally intentional: If name lookup finds these, they better be "of the right signature" to detect user errors (e.g. missing const on the declaration) early instead of falling back to random other overloads.
changed the title [-][stmt.expand] `begin(E)`/`end(E)` should perform overload resolution[/-][+]CWG3123 [stmt.expand] `begin(E)`/`end(E)` should perform overload resolution[/+]on Nov 17, 2025
Activity
Sirraide commentedon Nov 11, 2025
CC @Endilll, @katzdm
t3nsor commentedon Nov 11, 2025
This is not the only example of core language rules that are based on a name being found rather than whether overload resolution succeeds. We also have them in e.g. [expr.await] and [dcl.struct.bind]. I think there must be a reason why CWG likes it this way, even though I don't know what it is.
jensmaurer commentedon Nov 11, 2025
We usually do that for member functions (I thought), so that we "lock in" to the begin/end interpretation even if, due to slight mishaps in the declaration, the eventual expression involving begin/end would fail to compile. This protects users against accidents.
brevzin commentedon Nov 14, 2025
Maybe something like:
Also we're not checking if
E.begin()orE.end()are actually viable either, since that part just checks if they exist at all. So maybe something similar for the first bullet?jensmaurer commentedon Nov 14, 2025
That's generally intentional: If name lookup finds these, they better be "of the right signature" to detect user errors (e.g. missing const on the declaration) early instead of falling back to random other overloads.
jensmaurer commentedon Nov 17, 2025
CWG3123
[-][stmt.expand] `begin(E)`/`end(E)` should perform overload resolution[/-][+]CWG3123 [stmt.expand] `begin(E)`/`end(E)` should perform overload resolution[/+]