ISO/ IEC JTC1/SC22/WG21 p0090r0

Document number: P0090R0
Date: 2015-09-24
Project: Programming Language C++, Library Evolution Working Group
Reply-to: Stephan T. Lavavej <stl@microsoft.com>


Removing result_type, etc.


I. Introduction

The C++ Standard is big.  We should make it smaller by removing stuff we don't
need anymore, like typedefs that have become harmful to generic code.
This will guide users towards modern techniques.


II. Rationale

Q1. What are you proposing?

A1. There are four groups of edits being proposed here:

* Removing every mention of result_type, argument_type, first_argument_type,
and second_argument_type, except for leaving <random> untouched.  Almost all of
the removals are within 20.9 [function.objects].  There are a few more removals
within owner_less, map::value_compare, and multimap::value_compare.

* Removing the negators not1() and not2(), which were powered by these
typedefs.

* Listing these removals in Annex C: Compatibility.

* Adding "weasel wording" to allow implementations to continue to provide the
removed machinery (if they want) while remaining conformant to C++17, and to
prevent users from doing things like macroizing argument_type (which would
interfere with such backwards-compatible implementations).  Additionally, this
covers the previous removals of auto_ptr, etc. (see [1]).


Q2. What's wrong with result_type, etc.?

A2. These C++98/03/TR1-era typedefs predated decltype and perfect forwarding.
Previously, generic code had to request information from function objects
before adapting them.  Now, manually communicating that information is
unnecessary.  decltype supersedes result_type, because the compiler can simply
report what the result of calling a function object with specific arguments
will be.  And perfect forwarding supersedes the argument_type family, since
adaptors can simply take/store/forward arbitrary arguments.

In fact, these typedefs are worse than useless.  They're counterproductive,
because many callable objects lack them.  Function pointers and pointers to
members have always lacked them.  ptr_fun(), which wrapped function pointers
with these typedefs, was recently removed (see [1] again).  Most importantly,
lambdas have always lacked these typedefs, and they are the most important
function objects of all.  Generic lambdas are even more incompatible.

What this means is that if a user attempts to write generic code by using the
result_type family of typedefs, their code won't be generic - it'll fail to
handle lambdas, generic lambdas, function pointers, etc.

These typedefs should be removed because they've become actively harmful to
modern code.


Q3. What's wrong with the negators not1() and not2()?

A3. They're the last surviving consumers of argument_type, etc. in the
Standard.  They're also near-useless now that generic lambdas can easily negate
things (including other lambdas, as usual).  not_fn() has also superseded them
(see [2], 4.4 [func.not_fn]).

We don't need to wait for not_fn() to be added to the Standard.  We can remove
not1() and not2() now, since generic lambdas can indeed do the same work.


Q4. What's happening to <random>?

A4. Nothing.  Its usage of result_type is extensive and non-problematic for
users (since URNGs and so forth can't be plain functions or lambdas).
<random> is unaffected by the changes being proposed here, even indirectly.
For example, while reference_wrapper's "weak result type" wording is being
removed, reference_wrapper already couldn't meet the requirements for seed
sequences (no .generate()), URNGs (no ::min()), or distributions (no .reset()).


III. Standardese

(Edit 1) In 20.8.2.4 [util.smartptr.ownerless]/1, perform two deletions:

"template<class T> struct owner_less<shared_ptr<T> > {
<DEL>
  typedef bool result_type;
  typedef shared_ptr<T> first_argument_type;
  typedef shared_ptr<T> second_argument_type;
</DEL>
  bool operator()(shared_ptr<T> const&, shared_ptr<T> const&) const;
  bool operator()(shared_ptr<T> const&, weak_ptr<T> const&) const;
  bool operator()(weak_ptr<T> const&, shared_ptr<T> const&) const;
};

template<class T> struct owner_less<weak_ptr<T> > {
<DEL>
  typedef bool result_type;
  typedef weak_ptr<T> first_argument_type;
  typedef weak_ptr<T> second_argument_type;
</DEL>
  bool operator()(weak_ptr<T> const&, weak_ptr<T> const&) const;
  bool operator()(shared_ptr<T> const&, weak_ptr<T> const&) const;
  bool operator()(weak_ptr<T> const&, shared_ptr<T> const&) const;
};"

(Edit 2) Delete 20.9 [function.objects]/5 completely:

"<DEL>[ Note: To enable adaptors and other components to manipulate function
objects that take one or two arguments many of the function objects in this
clause correspondingly provide typedefs argument_type and result_type for
function objects that take one argument and first_argument_type,
second_argument_type, and result_type for function objects that take two
arguments. -end note ]</DEL>"

(Edit 3) Delete 20.9.2 [func.require]/3 completely, including its bullet
points:

"<DEL>If a call wrapper (20.9.1) has a weak result type the type of its member
type result_type is based on the type T of the wrapper's target object
(20.9.1):
- if T is a pointer to function type, result_type shall be a synonym for the
return type of T;
- if T is a pointer to member function, result_type shall be a synonym for the
return type of T;
- if T is a class type and the qualified-id T::result_type is valid and denotes
a type (14.8.2), then result_type shall be a synonym for T::result_type;
- otherwise result_type shall not be defined.</DEL>"

(Edit 4) In 20.9.4 [refwrap], perform one deletion:

"template <class T> class reference_wrapper {
public :
  // types
  typedef T type;
<DEL>
  typedef see below result_type;          // not always defined
  typedef see below argument_type;        // not always defined
  typedef see below first_argument_type;  // not always defined
  typedef see below second_argument_type; // not always defined
</DEL>

  // construct/copy/destroy
  reference_wrapper(T&) noexcept;"

(Edit 5) Delete 20.9.4 [refwrap]/3-5 completely, including their bullet points:

"<DEL>reference_wrapper<T> has a weak result type (20.9.2). If T is a function
type, result_type shall be a synonym for the return type of T.

The template specialization reference_wrapper<T> shall define a nested type
named argument_type as a synonym for T1 only if the type T is any of the
following:
- a function type or a pointer to function type taking one argument of type T1
- a pointer to member function R T0::f cv (where cv represents the member
function's cv-qualifiers); the type T1 is cv T0*
- a class type where the qualified-id T::argument_type is valid and denotes a
type (14.8.2); the type T1 is T::argument_type.

The template instantiation reference_wrapper<T> shall define two nested types
named first_argument_type and second_argument_type as synonyms for T1 and T2,
respectively, only if the type T is any of the following:
- a function type or a pointer to function type taking two arguments of types
T1 and T2
- a pointer to member function R T0::f(T2) cv (where cv represents the member
function's cv-qualifiers); the type T1 is cv T0*
- a class type where the qualified-ids T::first_argument_type and
T::second_argument_type are both valid and both denote types (14.8.2); the type
T1 is T::first_argument_type and the type T2 is T::second_argument_type.</DEL>"

(Edit 6) In the sections:

20.9.5 [arithmetic.operations]
20.9.6 [comparisons]
20.9.7 [logical.operations]
20.9.8 [bitwise.operations]

Delete every typedef named:

result_type
argument_type
first_argument_type
second_argument_type

(Edit 7) In 20.9.10.3 [func.bind.bind]/3, perform one deletion:

"Returns: A forwarding call wrapper g <DEL>with a weak result type
(20.9.2)</DEL>."

(Edit 8) In 20.9.10.3 [func.bind.bind]/7, perform one deletion:

"Returns: A forwarding call wrapper g <DEL>with a nested type result_type
defined as a synonym for R</DEL>."

(Edit 9) In 20.9.11 [func.memfn]/1, perform one deletion:

"Returns: A simple call wrapper (20.9.1) fn such that the expression
fn(t, a2, ..., aN) is equivalent to INVOKE (pm, t, a2, ..., aN) (20.9.2).
<DEL>fn shall have a nested type result_type that is a synonym for the return
type of pm when pm is a pointer to member function.</DEL>"

(Edit 10) Delete 20.9.11 [func.memfn]/2-3 completely:

"<DEL>The simple call wrapper shall define two nested types named argument_type
and result_type as synonyms for cv T* and Ret, respectively, when pm is a
pointer to member function with cv-qualifier cv and taking no arguments, where
Ret is pm's return type.

The simple call wrapper shall define three nested types named
first_argument_type, second_argument_type, and result_type as synonyms for
cv T*, T1, and Ret, respectively, when pm is a pointer to member function with
cv-qualifier cv and taking one argument of type T1, where Ret is pm's return
type.</DEL>"

(Edit 11) In 20.9.12.2 [func.wrap.func], perform one deletion:

"template<class R, class... ArgTypes>
class function<R(ArgTypes...)> {
public:
<DEL>
  typedef R result_type;
  typedef T1 argument_type;        // only if sizeof...(ArgTypes) == 1 and
                                   // the type in ArgTypes is T1
  typedef T1 first_argument_type;  // only if sizeof...(ArgTypes) == 2 and
                                   // ArgTypes contains T1 and T2
  typedef T2 second_argument_type; // only if sizeof...(ArgTypes) == 2 and
                                   // ArgTypes contains T1 and T2
</DEL>

  // 20.9.12.2.1, construct/copy/destroy:
  function() noexcept;"

(Edit 12) Delete the bullet point 20.9.13 [unord.hash]/1.3:

"<DEL>- provide two nested types result_type and argument_type which shall be
synonyms for size_t and Key, respectively,</DEL>"

(Edit 13) In 23.4.4.1 [map.overview]/2, perform one deletion:

"class value_compare {
friend class map;
protected:
  Compare comp;
  value_compare(Compare c) : comp(c) {}
public:
<DEL>
  typedef bool result_type;
  typedef value_type first_argument_type;
  typedef value_type second_argument_type;
</DEL>
  bool operator()(const value_type& x, const value_type& y) const {
    return comp(x.first, y.first);
  }
};"

(Edit 14) In 23.4.5.1 [multimap.overview]/2, perform one deletion:

"class value_compare {
friend class multimap;
protected:
  Compare comp;
  value_compare(Compare c) : comp(c) { }
public:
<DEL>
  typedef bool result_type;
  typedef value_type first_argument_type;
  typedef value_type second_argument_type;
</DEL>
  bool operator()(const value_type& x, const value_type& y) const {
    return comp(x.first, y.first);
  }
};"


(Edit 15) In 20.9 [function.objects]/2, perform one deletion:

"template <> struct bit_not<void>;

<DEL>
// 20.9.9, negators:
template <class Predicate> class unary_negate;
template <class Predicate>
  constexpr unary_negate<Predicate> not1(const Predicate&);
template <class Predicate> class binary_negate;
template <class Predicate>
  constexpr binary_negate<Predicate> not2(const Predicate&);
</DEL>

// 20.9.10, bind:
template<class T> struct is_bind_expression;"

(Edit 16) Delete the section 20.9.9 [negators] entirely.


(Edit 17) Under C.4 [diff.cpp14], add a new section between
C.4.1 [diff.cpp14.lex] and C.4.2 [diff.cpp14.depr]:

"<INS>
C.4.x Clause 20: general utilities library [diff.cpp14.utilities]

Change: The typedefs result_type, argument_type, first_argument_type, and
second_argument_type are not defined by function objects.
Rationale: Superseded by new features, including decltype and
forwarding references.
Effect on original feature: Valid C++ 2014 code that uses these typedefs may
fail to compile in this International Standard.

Change: The class templates unary_negate and binary_negate and the function
templates not1 and not2 are not defined.
Rationale: Superseded by new features, including generic lambdas.
Effect on original feature: Valid C++ 2014 code that uses these class templates
and function templates may fail to compile in this International Standard.
</INS>"


(Edit 18) Under 17.6.4.3 [reserved.names], add a new section between
17.6.4.3.1 [macro.names] and 17.6.4.3.2 [extern.names]:

"<INS>
17.6.4.3.x Zombie names [zombie.names]

In namespace std, the following names are reserved for previous
standardization: auto_ptr, bind1st, bind2nd, binder1st, binder2nd, mem_fun,
mem_fun_ref, mem_fun_t, mem_fun1_t, mem_fun_ref_t, mem_fun1_ref_t,
const_mem_fun_t, const_mem_fun1_t, const_mem_fun_ref_t, const_mem_fun1_ref_t,
not1, not2, unary_negate, binary_negate, ptr_fun, pointer_to_unary_function,
pointer_to_binary_function, random_shuffle, unary_function, and
binary_function.

It is implementation-defined whether function objects in the C++ standard
library additionally provide the following typedefs: result_type,
argument_type, first_argument_type, and second_argument_type.
</INS>"


IV. References

All of the Standardese citations in this proposal are to Working Paper N4527:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4527.pdf

[1] N4190 "Removing auto_ptr, random_shuffle(), And Old <functional> Stuff"
by Stephan T. Lavavej:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4190.htm

[2] N4529 "Working Draft, C++ Extensions for Library Fundamentals, Version 2"
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4529.html

(end)