3023. Clarify unspecified call wrappers

Section: 22.10.16 [func.memfn], 22.10.13 [func.not.fn], 22.10.15 [func.bind] Status: Resolved Submitter: Detlef Vollmann Opened: 2017-10-07 Last modified: 2021-06-06

Priority: 3

View all other issues in [func.memfn].

View all issues with Resolved status.

Discussion:

Even after the discussion on the reflector, starting with this reflector message it's not completely clear that unspecified as return type of mem_fn really means 'unspecified, but always the same'. The same problem exists for bind() and not_fn().

Possible solution:

Specify in 22.10.3 [func.def] or 22.10.4 [func.require] that a call wrapper type is always the same for forwarding call wrappers if the object is returned by a function with the same parameter types. And also put into [func.not_fn] that a call_wrapper object is a simple call wrapper.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3. Tomasz to write a paper that will address this issue. See also 3015

[2017-11-10, Tomasz comments and provides wording together with STL]

From the core language rules it is already required that same function template specialization have the same return type. Given that the invocation of mem_fn/bind/not_fn will always return the same wrapper type, if they are instantiated (called with) same parameters type. However, the existence of this issue, shows that some library-wide clarification note would be welcomed.

[2019-05-12; Tomasz comments]

I have realized that this issue indicates an real problem with the usability of bind as the replacement of the binder1st/binder2nd. Currently it is not required that a binding functor of the same type with same argument, produces the same result, as the type of the call wrapper may depend on the cv ref qualifiers of arguments. For example we are not requiring that the types of f1, f2, f3, f4 are the same (and actually they are not for clang):

auto func = [](std::string) {};
std::string s("foo");
auto f1 = std::bind(func, s);
auto f2 = std::bind(std::as_const(func), std::as_const(s));
auto f3 = std::bind(func, std::string("bar"));
auto f4 = std::bind(std::move(func), std::move(s));
// online link: https://wandbox.org/permlink/dcXJaITMJCnBWt7R

As a consequence, if the user creates a std::vector<decltype(std::bind(func, std::string(), _2))> (instead of std::vector<std::binder1st<FuncType, std::string>>) he may not be able to store the result of the binding func with std::string instance, if an copy of std::string is made. That leads me to conclusion that this issue actually require wording change, to provide such guarantee, and is materially different from LWG 3015.

During migration from std::bind1st/std::bind2nd (removed in C++17) to std::bind, the user may need to replace std::binder1st/std::binder2nd with an appropriate decltype of std::bind invocation. For example:

FuncType func; std::string s;

std::vector<std::binder1st<FuncType>> v;
v.push_back(std::bind1st(func, s));
v.push_back(std::bind1st(func, std::string("text")));
needs to be replaced with:
std::vector<decltype(std::bind(func, s, _1))> v;
v.push_back(std::bind(func, s, _1));
v.push_back(std::bind(func, std::string("text"), _1));

but the last statement is not guaranteed to be well-formed.

Therefore I would like to withdraw my previously suggested wording change.

Previous resolution [SUPERSEDED]:

This wording is relative to N4700.

  1. After section [expos.only.types] "Exposition-only types" add the following new section:

    ?.?.?.?.? unspecified types [unspecified.types]

    [Note: Whenever the return type of a function template is declared as unspecified, the return type depends only on the template arguments of the specialization. Given the example:

    template<class T> unspecified f(T);
    

    the expressions f(0) and f(1) have the same type. — end note]

[2020-01 Resolved by the adoption of P1065 in Cologne.]

Proposed resolution: