Document #: | P3171R1 |
Date: | 2024-06-04 |
Project: | Programming Language C++ |
Audience: |
LEWG |
Reply-to: |
Peter Dimov <pdimov@gmail.com> Barry Revzin <barry.revzin@gmail.com> |
As noted in [P2760R0], there are a lot of function objects for operators in the standard library, but several operators are missing. This paper proposes to add the functionality for all the missing operators, but to also do it in a different way than simply by adding function objects.
[Boost.Lambda2] is a Boost library
(written by Peter) which makes it possible to write very terse, simple
operations, by building upon the
std::bind
machinery. When Barry was implementing std::views::zip
[P2321R2], a range adaptor whose
implementation requires forwarding various operators across a
tuple
, Boost.Lambda2 provided a very
nice way to implement those operations. Here is a comparison between a
hand-written lambda solution, function objects, and the placeholder
solution that Lambda2 offers:
Handwritten Lambdas | Named Function Objects | Boost.Lambda2 |
---|---|---|
|
|
|
|
|
|
It’s not just that the Lambda2 alternatives are overwhelmingly terser
(it’s very hard to beat 3 characters for the dereference operation,
especially compared to the handwritten lambda that must use -> decltype(auto)
and is thus 46 characters long), they more directly express exactly the
work being done.
Lambda2 also offers a more expressive way of doing common predicates, even in the case where the named function object already exists. Let’s take an example where you want to write a predicate for if the argument is negative (an example Barry previously wrote about on his blog here), there are several ways to do it:
// hand-written lambda (28 characters) [](auto e) { return e < 0; } // attempting to use std::less{} (19 characters, but... uh...) (less{}, _1, 0) bind // Boost.Lambda2 (6 characters) < 0 _1
It also allows for an approach to address the question of
projections. Let’s say that rather than finding a negative number, we
want to find a Point
whose
x
coordinate is negative:
// hand-written lambda (30 characters) (points, [](Point p){ return p.x < 0; }) find_if // Boost.Lambda 2 (18 characters) (points, _1->*&Point::x < 0) find_if
Or if the x
coordinate is 0:
// hand-written lambda (31 characters) (points, [](Point p){ return p.x == 0; }) find_if // using projection (12 characters, but cryptic) (points, 0, &Point::x); find // Boost.Lambda 2 (19 characters) (points, _1->*&Point::x == 0) find_if
Note that this latter usage could be improved significantly with
something like [P0060R0], which would actually allow for
writing the predicate _1.x == 0
.
Which is difficult to beat.
You can see more examples in the [Boost.Lambda2] docs.
We propose to solve the issue of missing operator function objects in
the standard library, as well as less-than-ergonomic lambda syntax for
common predicates, by standardizing Boost.Lambda2. That is not a large
proposal. The standard library already provides placeholders, std::placeholders::_1
and friends. The standard library also already provides
std::bind
,
which is already implemented in a way that supports composition of bind
expressions. All we need to do is add operators.
We additionally add the missing operator function objects. Now, most of the missing operator function objects and placeholder operators are easy enough to add, except one: taking an object’s address.
&x
Now, this particular operator has two problems. First, making
&_1
work
requires overload unary operator&()
and that seems particularly questionable, even in cases like this. And
in order to make this broadly useful, we couldn’t just overload it as a
member function, it’d have to be a non-member - to support things like
&*_1
or
any other combination of operations (which is part of the value of
Lambda2). That’s a bit too much code for having
&x
not
actually mean address-of.
We could potentially address this problem by adding in a function
like std::placeholders::addr(x)
to mean addressof, so that instead of the cute
&_1
syntax you’d have to write addr(_1)
,
which doesn’t have any issues with
&
. Note
that we cannot call this function
addressof
because while addressof(_1)
would be okay, addressof(addressof(_1))
would become ambiguous (unless we also change std::addressof
, as
we’re about to discuss).
Second, the obvious name for a function object taking the address of
an object would be std::addressof
-
but that already exists, as a function template. We cannot change std::addressof
to
be a type - that would break all code that uses it. We could potentially
change it to be an object - that would break only ADL uses of it, but
given the nature of std::addressof
those seem pretty unlikely to be common, so it’s potentially a feasible
route to take. It would also allow _1->*std::addressof
(in the absence of addr(_1)
or similar formulation) as a short-ish way of expressing this.
For now, we’re going to punt on both problems and simply not support either a terse addressof on placeholders or providing an addressof function object.
Boost.Lambda2 additionally provides two helper function objects:
first
and
second
, such that _1->*first
gives you the first element of the type (as by std::get<0>
)
and _1->*second
gives you the second. This is done by just providing function objects
that perform these operations, similar to the proposed
get_key
and
get_value
[P2769R1].
Also, while most operators take forwarding references, there are two
additional overloads of
>>
and
<<
which are special-cased such that operations like std::cout << _1
work and capture
std::cout
by
reference. The special-casing is necessary because otherwise
std::cout
would be captured by value, which is not allowed, and users would have
to write std::ref(std::cout) << _1
.
We propose the new function objects as transparent, non-templated
types. This follows the precedent of
compare_three_way
.
Due to the way name lookup in the presence of using directives works,
for the operators to be reliably found, placeholders and bind
expressions (the types returned from
std::bind
)
need to have std::placeholders
as an associated namespace, even if using namespace std::placeholders;
is in effect.
This already happens to be true (by chance) under libc++, where
_1
is of type std::placeholders::__ph<1>
,
and std::bind(f, _1)
is of type std::__bind<void(&)(int), std::placeholders::__ph<1> const&>
.
It’s however not true for libstdc++ (std::_Placeholder<1>
and std::_Bind<void(*)(int)(std::_Placeholder<1>)>
,
respectively) or MSSTL (std::_Ph<1>
and std::_Binder<std::_Unforced,void (__cdecl&)(int),std::_Ph<1> const &>
).
Since the types of the standard placeholders and the bind expressions
produced by
std::bind
are deliberately left unspecified by the standard, it would be
conforming for implementations to change the types of
e.g. _1
to either refer to a type in
std::placeholders
,
or otherwise have std::placeholders
as the associated namespace. Their old types can be retained for
compatibility, and will continue to work because std::is_placeholder
is specialized for them. (The same holds for the return type of
std::bind
,
if it’s changed to also have std::placeholders
as the associated namespace in the unlikely event of users wanting to do
something like std::bind(f, 1) == std::bind(g, 1)
.)
At the moment we don’t yet propose formal wording for this associated namespace requirement, because we aren’t sure whether we need one, or if we do, what form will be preferred.
Has been shipping in Boost since 1.77 (August 2021).
Extend 22.10.2 [functional.syn] to add the additional function objects:
namespace std { // ... // [bitwise.operations], bitwise operations template<class T = void> struct bit_and; // freestanding template<class T = void> struct bit_or; // freestanding template<class T = void> struct bit_xor; // freestanding template<class T = void> struct bit_not; // freestanding template<> struct bit_and<void>; // freestanding template<> struct bit_or<void>; // freestanding template<> struct bit_xor<void>; // freestanding template<> struct bit_not<void>; // freestanding + // [additional.operations], additional transparent operations + struct subscript; // freestanding + struct left_shift; // freestanding + struct right_shift; // freestanding + struct unary_plus; // freestanding + struct dereference; // freestanding + struct increment; // freestanding + struct decrement; // freestanding + struct postfix_increment; // freestanding + struct postfix_decrement; // freestanding + + // [compound.operations], compound assignment operations + struct plus_equal; // freestanding + struct minus_equal; // freestanding + struct multiplies_equal; // freestanding + struct divides_equal; // freestanding + struct modulus_equal; // freestanding + struct bit_and_equal; // freestanding + struct bit_or_equal; // freestanding + struct bit_xor_equal; // freestanding + struct left_shift_equal; // freestanding + struct right_shift_equal; // freestanding // ... }
Extend 22.10.2 [functional.syn] to add operators:
namespace std { // ... namespace placeholders { // M is the implementation-defined number of placeholders see below _1; // freestanding see below _2; // freestanding . . . see below _M; // freestanding + template<class A, class B> constexpr auto operator+(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator-(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator*(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator/(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator%(A&&, B&&); // freestanding + template<class A> constexpr auto operator-(A&&); // freestanding + + template<class A, class B> constexpr auto operator==(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator!=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator<(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator>(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator<=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator>=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator<=>(A&&, B&&); // freestanding + + template<class A, class B> constexpr auto operator&&(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator||(A&&, B&&); // freestanding + template<class A> constexpr auto operator!(A&&); // freestanding + + template<class A, class B> constexpr auto operator&(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator|(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator^(A&&, B&&); // freestanding + template<class A> constexpr auto operator~(A&&); // freestanding + + template<class A, class B> constexpr auto operator<<(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator<<(A&, B&&); // freestanding + + template<class A, class B> constexpr auto operator>>(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator>>(A&, B&&); // freestanding + + template<class A> constexpr auto operator+(A&&); // freestanding + template<class A> constexpr auto operator*(A&&); // freestanding + template<class A> constexpr auto operator++(A&&); // freestanding + template<class A> constexpr auto operator--(A&&); // freestanding + template<class A> constexpr auto operator++(A&&, int); // freestanding + template<class A> constexpr auto operator--(A&&, int); // freestanding + + template<class A, class B> constexpr auto operator+=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator-=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator*=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator/=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator%=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator&=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator|=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator^=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator<<=(A&&, B&&); // freestanding + template<class A, class B> constexpr auto operator>>=(A&&, B&&); // freestanding + + template<class A, class B> constexpr auto operator->*(A&&, B&&); // freestanding + + inline constexpr unspecified first = unspecified; // freestanding + inline constexpr unspecified second = unspecifeid; // freestanding } // ... }
Add two new sections after 22.10.11 [bitwise.operations]:
Additional operations [additional.operations]
Class
subscript
[additional.operations.subscript]struct subscript { template<class T, class... U> constexpr auto operator()(T&& t, U&&... u) const -> decltype(std::forward<T>(t)[std::forward<U>(u)...]); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t)[std::forward<U>(u)]);
1 Returns:
std::forward<T>(t)[std::forward<U>(u)]
.Class
left_shift
[additional.operations.left_shift]struct left_shift { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) << std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) << std::forward<U>(u));
1 Returns:
std::forward<T>(t) << std::forward<U>(u)
.Class
right_shift
[additional.operations.right_shift]struct right_shift { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) >> std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) >> std::forward<U>(u));
1 Returns:
std::forward<T>(t) >> std::forward<U>(u)
.Class
unary_plus
[additional.operations.unary_plus]struct unary_plus { template<class T> constexpr auto operator()(T&& t) const -> decltype(+std::forward<T>(t)); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(+std::forward<T>(t));
1 Returns:
+std::forward<T>(t)
.Class
dereference
[additional.operations.dereference]struct dereference { template<class T> constexpr auto operator()(T&& t) const -> decltype(*std::forward<T>(t)); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(*std::forward<T>(t));
1 Returns:
*std::forward<T>(t)
.Class
increment
[additional.operations.increment]struct increment { template<class T> constexpr auto operator()(T&& t) const -> decltype(++std::forward<T>(t)); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(++std::forward<T>(t));
1 Returns:
++std::forward<T>(t)
.Class
decrement
[additional.operations.decrement]struct decrement { template<class T> constexpr auto operator()(T&& t) const -> decltype(--std::forward<T>(t)); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(--std::forward<T>(t));
1 Returns:
--std::forward<T>(t)
.Class
postfix_increment
[additional.operations.postfix_increment]struct postfix_increment { template<class T> constexpr auto operator()(T&& t) const -> decltype(std::forward<T>(t)++); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(std::forward<T>(t)++);
1 Returns:
std::forward<T>(t)++
.Class
postfix_decrement
[additional.operations.postfix_decrement]struct postfix_decrement { template<class T> constexpr auto operator()(T&& t) const -> decltype(std::forward<T>(t)--); using is_transparent = unspecified; };
template<class T> constexpr auto operator()(T&& t) const -> decltype(std::forward<T>(t)--);
1 Returns:
std::forward<T>(t)--
.Compound assignment operations [compound.operations]
Class
plus_equal
[compound.operations.plus_equal]struct plus_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) += std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) += std::forward<U>(u));
1 Returns:
std::forward<T>(t) += std::forward<U>(u)
.Class
minus_equal
[compound.operations.minus_equal]struct minus_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) -= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) -= std::forward<U>(u));
1 Returns:
std::forward<T>(t) -= std::forward<U>(u)
.Class
multiplies_equal
[compound.operations.multiplies_equal]struct multiplies_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) *= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) *= std::forward<U>(u));
1 Returns:
std::forward<T>(t) *= std::forward<U>(u)
.Class
divides_equal
[compound.operations.divides_equal]struct divides_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) /= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) /= std::forward<U>(u));
1 Returns:
std::forward<T>(t) /= std::forward<U>(u)
.Class
modulus_equal
[compound.operations.modulus_equal]struct modulus_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) %= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) %= std::forward<U>(u));
1 Returns:
std::forward<T>(t) %= std::forward<U>(u)
.Class
bit_and_equal
[compound.operations.bit_and_equal]struct bit_and_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) &= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) &= std::forward<U>(u));
1 Returns:
std::forward<T>(t) &= std::forward<U>(u)
.Class
bit_or_equal
[compound.operations.bit_or_equal]struct bit_or_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) |= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) |= std::forward<U>(u));
1 Returns:
std::forward<T>(t) |= std::forward<U>(u)
.Class
bit_xor_equal
[compound.operations.bit_xor_equal]struct bit_xor_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) ^= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) ^= std::forward<U>(u));
1 Returns:
std::forward<T>(t) ^= std::forward<U>(u)
.Class
left_shift_equal
[compound.operations.left_shift_equal]struct left_shift_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) <<= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) <<= std::forward<U>(u));
1 Returns:
std::forward<T>(t) <<= std::forward<U>(u)
.Class
right_shift_equal
[compound.operations.right_shift_equal]struct right_shift_equal { template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) >>= std::forward<U>(u)); using is_transparent = unspecified; };
template<class T, class U> constexpr auto operator()(T&& t, U&& u) const -> decltype(std::forward<T>(t) >>= std::forward<U>(u));
1 Returns:
std::forward<T>(t) >>= std::forward<U>(u)
.
Extend 22.10.15.5 [func.bind.place]:
namespace std::placeholders { // M is the number of placeholders+ template <int J> + struct placeholder { // exposition only + template <class... Args> + constexpr decltype(auto) operator()(Args&&... ) const noexcept; + template <class... T> + constexpr auto operator[](T&&...) const; + }; see below _1; see below _2; . . . see below _M; }
1 The number
M
of placeholders is implementation-defined.2 All placeholder types meet the Cpp17DefaultConstructible and Cpp17CopyConstructible requirements, and their default constructors and copy/move constructors are constexpr functions that do not throw exceptions. It is implementation-defined whether placeholder types meet the Cpp17CopyAssignable requirements, but if so, their copy assignment operators are constexpr functions that do not throw exceptions.
3 Placeholders should be defined as:
- inline constexpr unspecified _1{}; + inline constexpr placeholder<1> _1{};
If they are not, they are declared as:
- extern unspecified _1; + extern placeholder<1> _1;
4 Placeholders are freestanding items ([freestanding.item]).
template <int J> template <class... Args> decltype(auto) placeholder<J>::operator()(Args&&... args) const noexcept;
5 Constraints:
sizeof...(Args) >= J
istrue
.6 Returns:
std::forward<Args>(args)...[J - 1]
.template <int J> template <class... T> auto placeholder<J>::operator[](T&&... t) const;
7 Returns:
bind(subscript(), *this, std::forward<T>(t)...)
.8 Each operator function declared in this clause is constrained on at least one of the parameters having a type
T
which satisfiesis_placeholder_v<remove_cvref_t<T>> || is_bind_expression_v<remove_cvref_t<T>>
istrue
.template<class A, class B> constexpr auto operator+(A&& a, B&& b);
9 Returns:
bind(plus<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator-(A&& a, B&& b);
10 Returns:
bind(minus<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator*(A&& a, B&& b);
11 Returns:
bind(multiplies<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator/(A&& a, B&& b);
12 Returns:
bind(divides<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator%(A&& a, B&& b);
13 Returns:
bind(modulus<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A> constexpr auto operator-(A&& a);
14 Returns:
bind(negate<>(), std::forward<A>(a))
.template<class A, class B> constexpr auto operator==(A&& a, B&& b);
15 Returns:
bind(equal_to<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator!=(A&& a, B&& b);
16 Returns:
bind(not_equal_to<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator<(A&& a, B&& b);
17 Returns:
bind(less<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator>(A&& a, B&& b);
18 Returns:
bind(greater<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator<=(A&& a, B&& b);
19 Returns:
bind(less_equal<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator>=(A&& a, B&& b);
20 Returns:
bind(greater_equal<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator<=>(A&& a, B&& b);
21 Returns:
bind(compare_three_way(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator&&(A&& a, B&& b);
22 Returns:
bind(logical_and<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator||(A&& a, B&& b);
23 Returns:
bind(logical_or<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A> constexpr auto operator!(A&& a);
24 Returns:
bind(logical_not<>(), std::forward<A>(a))
.template<class A, class B> constexpr auto operator&(A&& a, B&& b);
25 Returns:
bind(bit_and<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator|(A&& a, B&& b);
26 Returns:
bind(bit_or<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator^(A&& a, B&& b);
27 Returns:
bind(bit_xor<>(), std::forward<A>(a), std::forward<B>(b))
.template<class A> constexpr auto operator~(A&& a);
28 Returns:
bind(bit_not<>(), std::forward<A>(a))
.template<class A, class B> constexpr auto operator<<(A&& a, B&& b);
29 Constraints:
is_base_of_v<ios_base, remove_cvref_t<A>>
isfalse
.30 Returns:
bind(left_shift(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator<<(A& a, B&& b);
31 Constraints:
is_base_of_v<ios_base, remove_cvref_t<A>>
istrue
.32 Returns:
bind(left_shift(), ref(a), std::forward<B>(b))
.33 [ Note 1: This overload allows expressions like
std::cout << _1 << '\n'
to work. — end note ]template<class A, class B> constexpr auto operator>>(A&& a, B&& b);
34 Constraints:
is_base_of_v<ios_base, remove_cvref_t<A>>
isfalse
.35 Returns:
bind(right_shift(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator>>(A& a, B&& b);
36 Constraints:
is_base_of_v<ios_base, remove_cvref_t<A>>
istrue
.37 Returns:
bind(right_shift(), ref(a), std::forward<B>(b))
.template<class A> constexpr auto operator+(A&& a);
38 Returns:
bind(unary_plus(), std::forward<A>(a))
.template<class A> constexpr auto operator*(A&& a);
39 Returns:
bind(dereference(), std::forward<A>(a))
.template<class A> constexpr auto operator++(A&& a);
40 Returns:
bind(increment(), std::forward<A>(a))
.template<class A> constexpr auto operator--(A&& a);
41 Returns:
bind(decrement(), std::forward<A>(a))
.template<class A> constexpr auto operator++(A&& a, int);
42 Returns:
bind(postfix_increment(), std::forward<A>(a))
.template<class A> constexpr auto operator--(A&& a, int);
43 Returns:
bind(postfix_decrement(), std::forward<A>(a))
.template<class A, class B> constexpr auto operator+=(A&& a, B&& b);
44 Returns:
bind(plus_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator-=(A&& a, B&& b);
45 Returns:
bind(minus_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator*=(A&& a, B&& b);
46 Returns:
bind(multiplies_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator/=(A&& a, B&& b);
47 Returns:
bind(divides_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator%=(A&& a, B&& b);
48 Returns:
bind(modulus_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator&=(A&& a, B&& b);
49 Returns:
bind(bit_and_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator|=(A&& a, B&& b);
50 Returns:
bind(bit_or_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator^=(A&& a, B&& b);
51 Returns:
bind(bit_xor_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator<<=(A&& a, B&& b);
52 Returns:
bind(left_shift_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator>>=(A&& a, B&& b);
53 Returns:
bind(right_shift_equal(), std::forward<A>(a), std::forward<B>(b))
.template<class A, class B> constexpr auto operator->*(A&& a, B&& b);
54 Returns:
bind(std::forward<B>(b), std::forward<A>(a))
.55 The name
first
denotes a customization point object ([customization.point.object]). Given a subexpressionE
:
- (55.1) If
E
has class or enumeration type andget<0>(E)
is a valid expression where the meaning ofget
is established by performing argument-dependent lookup only ([basic.lookup.argdep]), thenfirst(E)
is expression-equivalent toget<0>(E)
.- (55.2) Otherwise,
first(E)
is ill-formed.56 The name
second
denotes a customization point object ([customization.point.object]). Given a subexpressionE
:
Add an entry to 17.3.2 [version.syn] for this
+ #define __cpp_lib_placeholder_operators 2024XXL // freestanding, also in <functional>