INVOKE
operation and union
typesSection: 22.10.4 [func.require] Status: C++23 Submitter: Jiang An Opened: 2021-12-29 Last modified: 2023-11-22
Priority: 3
View all other issues in [func.require].
View all issues with C++23 status.
Discussion:
There are two cases of the INVOKE
operation specified with std::is_base_of_v
(22.10.4 [func.require] (1.1), (1,4)), which means the following code snippet is ill-formed, as
std::is_base_of_v<B, D>
is false
when either B
or D
is a
union
type.
union Foo { int x; }; static_assert(std::is_invocable_v<int Foo::*, Foo&>);
Currently libstdc++ accepts this code, because it uses slightly different conditions that handle union
types. libc++ and MSVC STL reject this code as specified in 22.10.4 [func.require].
union
types?
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
[2023-02-07; Jonathan adds wording]
This is a regression introduced by LWG 2219.
In C++14 std::result_of<int Foo::*(Foo&)>::type
was valid, because the INVOKE
wording used to say
"f
is a pointer to member data of a class T
and t1
is an object of type T
or a reference to an object of type T
or a reference to an object of a type derived from T
".
Since LWG 2219 we use is_base_of
which is always false for
union types.
I don't think LWG 2219 intended to break this case, so we should fix it.
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
Modify 22.10.4 [func.require] as indicated:
-1- Define
INVOKE(f, t1, t2, …, tN)
as follows:
- (1.1) —
(t1.*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andis_same_v<T, remove_cvref_t<decltype(t1)>> || is_base_of_v<T, remove_reference_t<decltype(t1)>>
istrue
;- (1.2) —
(t1.get().*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andremove_cvref_t<decltype(t1)>
is a specialization ofreference_wrapper
;- (1.3) —
((*t1).*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andt1
does not satisfy the previous two items;- (1.4) —
t1.*f
whenN == 1
andf
is a pointer to data member of a classT
andis_same_v<T, remove_cvref_t<decltype(t1)>> || is_base_of_v<T, remove_reference_t<decltype(t1)>>
istrue
;- (1.5) —
t1.get().*f
whenN == 1
andf
is a pointer to data member of a classT
andremove_cvref_t<decltype(t1)>
is a specialization ofreference_wrapper
;- (1.6) —
(*t1).*f
whenN == 1
andf
is a pointer to data member of a classT
andt1
does not satisfy the previous two items;- (1.7) —
f(t1, t2, …, tN)
in all other cases.
[2023-02-07; Jonathan provides wording change requested by LWG]
Change remove_reference_t
to remove_cvref_t
.
is_base_of
ignores cv-qualifiers, so this isn't necessary,
but just using the same transformation in both cases seems simpler to grok.
[Issaquah 2023-02-07; LWG]
Move to Immediate for C++23
[2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Immediate → WP.]
Proposed resolution:
This wording is relative to N4928.
Modify 22.10.4 [func.require] as indicated:
-1- Define
INVOKE(f, t1, t2, …, tN)
as follows:
- (1.1) —
(t1.*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andis_same_v<T, remove_cvref_t<decltype(t1)>> || is_base_of_v<T, remove_
isreferencecvref_t<decltype(t1)>>true
;- (1.2) —
(t1.get().*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andremove_cvref_t<decltype(t1)>
is a specialization ofreference_wrapper
;- (1.3) —
((*t1).*f)(t2, …, tN)
whenf
is a pointer to a member function of a classT
andt1
does not satisfy the previous two items;- (1.4) —
t1.*f
whenN == 1
andf
is a pointer to data member of a classT
andis_same_v<T, remove_cvref_t<decltype(t1)>> || is_base_of_v<T, remove_
isreferencecvref_t<decltype(t1)>>true
;- (1.5) —
t1.get().*f
whenN == 1
andf
is a pointer to data member of a classT
andremove_cvref_t<decltype(t1)>
is a specialization ofreference_wrapper
;- (1.6) —
(*t1).*f
whenN == 1
andf
is a pointer to data member of a classT
andt1
does not satisfy the previous two items;- (1.7) —
f(t1, t2, …, tN)
in all other cases.