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.

  1. Edit 22.10.4 [func.require]:

    Define _INVOKE_(f, t1, t2, ..., tN) as follows:

    • (t1.*f)(t2, ..., tN) when f is a pointer to a member function 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 U or an object of type reference_wrapper<U>or a reference to an object of type reference_wrapper<U> where U is either the type T or a type derived from T;
    • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
    • t1.*f when N == 1 and f is a pointer to member data of a class T and t1is 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 U or an object of type reference_wrapper<U>or a reference to an object of type reference_wrapper<U> where U is either the type T or a type derived from T;
    • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1is 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.

  1. Edit 22.10.4 [func.require]:

    Define _INVOKE_(f, t1, t2, ..., tN) as follows:

    • (t1.*f)(t2, ..., tN) when f is a pointer to a member function 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;
    • (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of class Tand t1 is an object of type reference_wrapper<U> where Uis either the type T or a type derived from T.
    • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
    • t1.*f when N == 1 and f is a pointer to member data of a class T and t1is 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;
    • t1.get().*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type reference_wrapper<U>where U is either the type T or a type derived from T.
    • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1is 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.

  1. Change 22.10.4 [func.require] p1 as depicted:

    Define _INVOKE_(f, t1, t2, ..., tN) as follows:

    • (t1.*f)(t2, ..., tN) when f is a pointer to a member function 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 Tis_base_of<T, decay_t<decltype(t1)>>::valueis true;
    • (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of a class T and decay_t<decltype(t1)> is a specialization of reference_wrapper;
    • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous itemdoes not satisfy the previous two items;
    • t1.*f when N == 1 and f is a pointer to member data of a class T and t1is 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 Tis_base_of<T, decay_t<decltype(t1)>>::valueis true;
    • t1.get().*f when N == 1 and f is a pointer to member data of a class T and decay_t<decltype(t1)> is a specialization of reference_wrapper;
    • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 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.