This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++17 status.
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 other active issues in [func.require].
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)whenfis a pointer to a member function of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromTUor an object of typereference_wrapper<U>or a reference to an object of typereference_wrapper<U>whereUis either the typeTor a type derived fromT;
((*t1).*f)(t2, ..., tN)whenfis a pointer to a member function of a classTandt1is not one of the types described in the previous item;
t1.*fwhenN == 1andfis a pointer to member data of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromTUor an object of typereference_wrapper<U>or a reference to an object of typereference_wrapper<U>whereUis either the typeTor a type derived fromT;
(*t1).*fwhenN == 1andfis a pointer to member data of a classTandt1is 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_wrappers
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)whenfis a pointer to a member function of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromT;
(t1.get().*f)(t2, ..., tN)whenfis a pointer to a member function of classTandt1is an object of typereference_wrapper<U>whereUis either the typeTor a type derived fromT.
((*t1).*f)(t2, ..., tN)whenfis a pointer to a member function of a classTandt1is not one of the types described in the previous item;
t1.*fwhenN == 1andfis a pointer to member data of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromT;
t1.get().*fwhenN == 1andfis a pointer to member data of a classTandt1is an object of typereference_wrapper<U>whereUis either the typeTor a type derived fromT.
(*t1).*fwhenN == 1andfis a pointer to member data of a classTandt1is 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)whenfis a pointer to a member function of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromTis_base_of<T, decay_t<decltype(t1)>>::valueis true;
(t1.get().*f)(t2, ..., tN)whenfis a pointer to a member function of a classTanddecay_t<decltype(t1)>is a specialization ofreference_wrapper;
((*t1).*f)(t2, ..., tN)whenfis a pointer to a member function of a classTandt1is not one of the types described in the previous itemdoes not satisfy the previous two items;
t1.*fwhenN == 1andfis a pointer to member data of a classTandt1is an object of typeTor a reference to an object of typeTor a reference to an object of a type derived fromTis_base_of<T, decay_t<decltype(t1)>>::valueis true;
t1.get().*fwhenN == 1andfis a pointer to member data of a classTanddecay_t<decltype(t1)>is a specialization ofreference_wrapper;
(*t1).*fwhenN == 1andfis a pointer to member data of a classTandt1is not one of the types described in the previous itemdoes not satisfy the previous two items;
f(t1, t2, ..., tN)in all other cases.