Issue 2219: INVOKE-ing a pointer to member with a reference_wrapper as the object expression (original) (raw)
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.
2219. _INVOKE_-ing a pointer to member with a reference_wrapper as the object expression
Section: 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
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).*fbecause 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 INVOKEit 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 areference_wrapper, so we probably need a separate bullet to handle thereference_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.
We would have to be more careful if we had to deal with pointers to members of 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.