2048. Unnecessary mem_fn overloads

Section: 23.14 [function.objects], 23.14.12 [func.memfn] Status: C++14 Submitter: Jonathan Wakely Opened: 2011-04-18 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [function.objects].

View all issues with C++14 status.

Discussion:

The mem_fn overloads for member functions are redundant and misleading and should be removed from the post-C++11 WP.

I believe the history of the overloads is as follows:

In TR1 and in C++0x prior to the N2798 draft, mem_fn was specified by a single signature:

template<class R, class T> unspecified mem_fn(R T::* pm);

and was accompanied by the remark "Implementations may implement mem_fn as a set of overloaded function templates." This remark predates variadic templates and was presumably to allow implementations to provide overloads for a limited number of function parameters, to meet the implementation-defined limit on numbers of template parameters.

N2770 "Concepts for the C++0x Standard Library: Utilities" added separate overloads for pointers to member functions, apparently so that function parameters would require the CopyConstructible concept (those overloads first appeared in N2322.) The overloads failed to account for varargs member functions (i.e. those declared with an ellipsis in the parameter-declaration-clause) e.g.

struct S {
 int f(int, ...);
};

Syntactically such a function would be handled by the original mem_fn(R T::* pm) signature, the only minor drawback being that there would be no CopyConstructible requirement on the parameter list. (Core DR 547 clarifies that partial specializations can be written to match cv-qualified and ref-qualified functions to support the case where R T::* matches a pointer to member function type.)

LWG issue 920 pointed out that additional overloads were missing for member functions with ref-qualifiers. These were not strictly necessary, because such functions are covered by the mem_fn(R T::* pm) signature.

Concepts were removed from the draft and N3000 restored the original single signature and accompanying remark.

LWG 1230 was opened to strike the remark again and to add an overload for member functions (this overload was unnecessary for syntactic reasons and insufficient as it didn't handle member functions with cv-qualifiers and/or ref-qualifiers.)

920 (and 1230) were resolved by restoring a full set of (non-concept-enabled) overloads for member functions with cv-qualifiers and ref-qualifiers, but as in the concept-enabled draft there were no overloads for member functions with an ellipsis in the parameter-declaration-clause. This is what is present in the FDIS.

Following the thread beginning with message c++std-lib-30675, it is my understanding that all the mem_fn overloads for member functions are unnecessary and were only ever added to allow concept requirements. I'm not aware of any reason implementations cannot implement mem_fn as a single function template. Without concepts the overloads are redundant, and the absence of overloads for varargs functions can be interpreted to imply that varargs functions are not intended to work with mem_fn. Clarifying the intent by adding overloads for varargs functions would expand the list of 12 redundant overloads to 24, it would be much simpler to remove the 12 redundant overloads entirely.

[Bloomington, 2011]

Move to Review.

The issue and resolution appear to be correct, but there is some concern that the wording of INVOKE may be different depending on whether you pass a pointer-to-member-data or pointer-to-member-function. That might make the current wording necessary after all, and then we might need to add the missing elipsis overloads.

There was some concern that the Remark confirming implementors had freedom to implement this as a set of overloaded functions may need to be restored if we delete the specification for these overloads.

[2012, Kona]

Moved to Tentatively Ready by the post-Kona issues processing subgroup.

[2012, Portland: applied to WP]

Proposed resolution:

This wording is relative to the FDIS.

  1. Change the <functional> synopsis 23.14 [function.objects] p. 2 as follows:

    namespace std {
      […]
      // [func.memfn], member function adaptors:
      template<class R, class T> unspecified mem_fn(R T::*);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...));
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile &&);
    
      […]
    }
    
  2. Change 23.14.12 [func.memfn] as follows:

    template<class R, class T> unspecified mem_fn(R T::*);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...));
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile &&);