INVOKE
-ing a pointer to member with a reference_wrapper
as the object expressionSection: 22.10.4 [func.require] Status: C++17 Submitter: Jonathan Wakely Opened: 2012-11-28 Last modified: 2017-07-30
Priority: 2
View all other issues in [func.require].
View all issues with C++17 status.
Discussion:
The standard currently requires this to be invalid:
#include <functional> struct X { int i; } x; auto f = &X::i; auto t1 = std::ref(x); int i = std::mem_fn(f)(t1);
The call expression on the last line is equivalent to INVOKE(f, std::ref(x))
which according to 22.10.4 [func.require]p1 results in the invalid expression (*t1).*f
because reference_wrapper<X>
is neither an object of type X
nor a reference
to an object of type X
nor a reference to an object of a type derived from X
.
The same argument applies to pointers to member functions, and if they don't work with INVOKE
it becomes harder to do all sorts of things such as:
call_once(o, &std::thread::join, std::ref(thr))
or
async(&std::list<int>::sort, std::ref(list));
The definition of INVOKE
should be extended to handle reference wrappers.
[2013-03-15 Issues Teleconference]
Moved to Review.
The wording seems accurate, but verbose. If possible, we would like to define the kind of thing being specified so carefully as one of a number of potential language constructs in a single place. It is also possible that this clause is that single place.
[2013-04-18, Bristol]
Jonathan comments:
In the proposed resolution in the first bullet (t1.*f)
is not valid if t1
is a
reference_wrapper
, so we probably need a separate bullet to handle the
reference_wrapper
case.
[2014-02-14, Issaquah, Mike Spertus supplies wording]
Previous resolution from Jonathan [SUPERSEDED]:
This wording is relative to N3485.
Edit 22.10.4 [func.require]:
Define
INVOKE(f, t1, t2, ..., tN)
as follows:
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
U
or an object of typereference_wrapper<U>
or a reference to an object of typereference_wrapper<U>
whereU
is either the typeT
or a type derived fromT
;
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;
t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
U
or an object of typereference_wrapper<U>
or a reference to an object of typereference_wrapper<U>
whereU
is either the typeT
or a type derived fromT
;
(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is not one of the types described in the previous item;
f(t1, t2, ..., tN)
in all other cases.
[2014-10-01, STL adds discussion and provides an improved resolution]
Because neither t1.*f
nor (*t1).*f
will compile when t1
is reference_wrapper<U>
for any U
, we don't need to inspect U
carefully. We can bluntly detect all reference_wrapper
s
and use get()
for them.
reference_wrapper
itself.
Fortunately, we don't. First, it doesn't have user-visible data members. Second, users technically can't take the
addresses of its member functions (this is a consequence of 16.4.6.5 [member.functions], the Implementer's Best Friend).
While we're in the neighborhood, I recommend simplifying and clarifying the wording used to detect base/derived objects.
Previous resolution from Mike Spertus [SUPERSEDED]:
This wording is relative to N3936.
Edit 22.10.4 [func.require]:
Define
INVOKE(f, t1, t2, ..., tN)
as follows:
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;
(t1.get().*f)(t2, ..., tN)
whenf
is a pointer to a member function of classT
andt1
is an object of typereference_wrapper<U>
whereU
is either the typeT
or a type derived fromT
.
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;
t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;
t1.get().*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typereference_wrapper<U>
whereU
is either the typeT
or a type derived fromT
.
(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is not one of the types described in the previous item;
f(t1, t2, ..., tN)
in all other cases.
[2015-02, Cologne]
Waiting for implementation experience.
[2015-05, Lenexa]
STL: latest note from Cologne, waiting for implementation experience
STL: don't think this is harder than anything else we do
MC: it does involve mem_fn
and invoke
STL: my simplication was not to attempt fine-grained
STL: can ignore pmf
STL: can't invoke pmf to reference wrapper
STL: wording dated back to TR1 when there was no decltype
MC: should decay_t<decltype(t1)>
be pulled out since it is in multiple places
STL: it could be handled editorially
STL: we fix function, bind, invoke
STL: have not implemented this but believe it is fine
MC: Eric F, you have worked in invoke
EF: yes, looks ok
MC: consensus move to ready
Proposed resolution:
This wording is relative to N3936.
Change 22.10.4 [func.require] p1 as depicted:
Define
INVOKE(f, t1, t2, ..., tN)
as follows:
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
is_base_of<T, decay_t<decltype(t1)>>::value
is true;
(t1.get().*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
anddecay_t<decltype(t1)>
is a specialization ofreference_wrapper
;
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous itemdoes not satisfy the previous two items;
t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
is_base_of<T, decay_t<decltype(t1)>>::value
is true;
t1.get().*f
whenN == 1
andf
is a pointer to member data of a classT
anddecay_t<decltype(t1)>
is a specialization ofreference_wrapper
;
(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is not one of the types described in the previous itemdoes not satisfy the previous two items;
f(t1, t2, ..., tN)
in all other cases.