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);