ISO/ IEC JTC1/SC22/WG21 N3749

Constexpr Library Additions: functional 
N3749 
2013-08-31
Marshall Clow, mclow.lists@gmail.com


Rationale:

The named C++ function objects (less, equals, greater_equal, etc) are
used throughout the standard library as an intermediate between code that needs
to manipulate objects and the fundamental operators (operator <, ==, >=
respectively).

In C++14, many of these fundamental operators have been marked as constexpr
(for some types; chrono, complex, optional, etc). But you if you use the
named operators, then you lose the ability to work at compile time.

For example:

	template <class T = void> struct less {
		typedef T first_argument_type;
		typedef T second_argument_type;
		typedef bool result_type;

		bool operator()(const T& x, const T& y) const { return x < y; }
		};

less<T>{}(x,y) can never be evaluated at compile time.

This is especially a problem for the proposed std::optional<T>, which defines
its' operator < as being constexpr, and yet requires it to use std::less to 
do the comparisons [ 20.6.8, p 4,5,6 ]. optional's operators cannot be executed
at compile time except for the simple case where the optional is disengaged.

I propose that we make the following change:

	template <class T = void> struct less {
		typedef T first_argument_type;
		typedef T second_argument_type;
		typedef bool result_type;

		constexpr bool operator()(const T& x, const T& y) const { return x < y; }
		};

This will allow people to use less<T>{}(x,y) at compile time.

This should also apply to all the relational operators (less, greater,
less_equal, greater_equal, not_equal_to, equal_to), the arithmetic
operators (plus, minus, multiplies, divides, modulus, negates), the bit
manipulations (bit_and, bit_or, bit_xor, bit_not) and the negators
(unary_negate, binary_negate, no1, not2).


Proposed Wording

All changes are relative to N3691):

=== Amend section 20.10.4 as follows:

template <class T = void> struct plus {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;
     
template <class T = void> struct minus {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;
  
template <class T = void> struct multiplies {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;
  
template <class T = void> struct divides {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;
  
template <class T = void> struct modulus {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;
  
template <class T = void> struct negate {
  <INS>constexpr</INS> T operator()(const T& x) const;

template <> struct plus<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct minus<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const
  
template <> struct multiplies<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct divides<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct modulus<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const
  
template <> struct negate<void> {
  template <class T> <INS>constexpr</INS> auto operator()(T&& t) const
  
=== Amend section 20.10.5 as follows:

template <class T = void> struct equal_to {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;
 
template <class T = void> struct not_equal_to {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct greater {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct less {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct greater_equal {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct less_equal {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <> struct equal_to<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct not_equal_to<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct greater<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct less<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct greater_equal<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct less_equal<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

=== Amend section 20.10.6 as follows:

template <class T = void> struct logical_and {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct logical_or {
  <INS>constexpr</INS> bool operator()(const T& x, const T& y) const;

template <class T = void> struct logical_not {
  <INS>constexpr</INS> bool operator()(const T& x) const;

template <> struct logical_and<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct logical_or<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct logical_not<void> {
  template <class T> <INS>constexpr</INS> auto operator()(T&& t) const
  
=== Amend section 20.10.7 as follows:

template <class T = void> struct bit_and {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;

template <class T = void> struct bit_or {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;

template <class T = void> struct bit_xor {
  <INS>constexpr</INS> T operator()(const T& x, const T& y) const;

template <class T = void> struct bit_not {
  <INS>constexpr</INS> T operator()(const T& x) const;

template <> struct bit_and<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct bit_or<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct bit_xor<void> {
  template <class T, class U> <INS>constexpr</INS> auto operator()(T&& t, U&& u) const

template <> struct bit_not<void> {
  template <class T> <INS>constexpr</INS> auto operator()(T&& t) const

=== Amend section 20.10.8 as follows:

template <class Predicate>
  class unary_negate {
  public:
     explicit <INS>constexpr</INS> unary_negate(const Predicate& pred);
     <INS>constexpr</INS> bool operator()(const typename Predicate::argument_type& x) const;

template <class Predicate>
   <INS>constexpr</INS> unary_negate<Predicate> not1(const Predicate& pred);

template <class Predicate>
  class binary_negate {
  public:
  explicit <INS>constexpr</INS> binary_negate(const Predicate& pred);
  <INS>constexpr</INS> bool operator()(const typename Predicate::first_argument_type& x,
      const typename Predicate::second_argument_type& y) const;

template <class Predicate>
   <INS>constexpr</INS> binary_negate<Predicate> not2(const Predicate& pred);