Created on 2025-09-24.00:00:00 last changed 5 days ago
Proposed resolution:
This wording is relative to N5014.
Modify [const.wrap.class], class template constant_wrapper synopsis, as indicated:
[Drafting note: The requires clause follows the form of `constant_wrapper`'s function call operator.]
struct cw-operators { // exposition only
[…]
// pseudo-mutators
template<constexpr-param T>
constexpr auto operator++(this T) noexcept requires requires(T::value_type x) { ++x; }
{ return constant_wrapper<[] { auto c = T::value; return ++c; }()>{}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept requires requires(T::value_type x) { x++; }
{ return constant_wrapper<[] { auto c = T::value; return c++; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept requires requires(T::value_type x) { --x; }
{ return constant_wrapper<[] { auto c = T::value; return --c; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept requires requires(T::value_type x) { x--; }
{ return constant_wrapper<[] { auto c = T::value; return c--; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator+=(this T, R) noexcept requires requires(T::value_type x) { x += R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v += R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator-=(this T, R) noexcept requires requires(T::value_type x) { x -= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v -= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator*=(this T, R) noexcept requires requires(T::value_type x) { x *= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v *= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator/=(this T, R) noexcept requires requires(T::value_type x) { x /= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v /= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator%=(this T, R) noexcept requires requires(T::value_type x) { x %= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v %= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator&=(this T, R) noexcept requires requires(T::value_type x) { x &= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v &= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator|=(this T, R) noexcept requires requires(T::value_type x) { x |= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v |= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator^=(this T, R) noexcept requires requires(T::value_type x) { x ^= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v ^= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator<<=(this T, R) noexcept requires requires(T::value_type x) { x <<= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v <<= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator>>=(this T, R) noexcept requires requires(T::value_type x) { x >>= R::value; }
{ return constant_wrapper<[] { auto v = T::value; return v >>= R::value; }()>{}; }
template<constexpr-param T>
constexpr auto operator++(this T) noexcept -> constant_wrapper<++Y> { return {}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept -> constant_wrapper<Y++> { return {}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept -> constant_wrapper<--Y> { return {}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept -> constant_wrapper<Y--> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator+=(this T, R) noexcept -> constant_wrapper<(T::value += R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator-=(this T, R) noexcept -> constant_wrapper<(T::value -= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator*=(this T, R) noexcept -> constant_wrapper<(T::value *= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator/=(this T, R) noexcept -> constant_wrapper<(T::value /= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator%=(this T, R) noexcept -> constant_wrapper<(T::value %= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator&=(this T, R) noexcept -> constant_wrapper<(T::value &= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator|=(this T, R) noexcept -> constant_wrapper<(T::value |= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator^=(this T, R) noexcept -> constant_wrapper<(T::value ^= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator<<=(this T, R) noexcept -> constant_wrapper<(T::value <<= R::value)> { return {}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator>>=(this T, R) noexcept -> constant_wrapper<(T::value >>= R::value)> { return {}; }
};
}
template<cw-fixed-value X, typename>
struct constant_wrapper: cw-operators {
static constexpr const auto & value = X.data;
using type = constant_wrapper;
using value_type = typename decltype(X)::type;
template<constexpr-param R>
constexpr auto operator=(R) const noexcept requires requires(value_type x) { x = R::value; }
{ return constant_wrapper<[] { auto v = value; return v = R::value; }()>{}; }
template<constexpr-param R>
constexpr auto operator=(R) const noexcept -> constant_wrapper<X = R::value> { return {}; }
constexpr operator decltype(auto)() const noexcept { return value; }
};
[ Kona 2025-11-08; Status changed: Immediate → WP. ]
[ Kona 2025-11-07; approved by LWG. Status changed: New → Immediate. ]
[ 2025-11-05; Zach provides improved wording ]
[ 2025-10-17; Reflector poll. ]
Set priority to 1 after reflector poll.
`operator+=` changed between P2781R4 and P2781R5, intent is unclear.
This wording is relative to N5014.
Modify [const.wrap.class], class template constant_wrapper synopsis, as indicated:
[Drafting note: The requires clause follows the form of `constant_wrapper`'s function call operator.]
struct cw-operators { // exposition only
[…]
// pseudo-mutators
template<constexpr-param T>
constexpr auto operator++(this T) noexcept
requires requires(T::value_type x) { constant_wrapper<++x>(); }
{ return constant_wrapper<[] { auto c = T::value; return ++c; }()>{}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept
requires requires(T::value_type x) { constant_wrapper<x++>(); }
{ return constant_wrapper<[] { auto c = T::value; return c++; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept
requires requires(T::value_type x) { constant_wrapper<--x>(); }
{ return constant_wrapper<[] { auto c = T::value; return --c; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept
requires requires(T::value_type x) { constant_wrapper<x-->(); }
{ return constant_wrapper<[] { auto c = T::value; return c--; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator+=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x += R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v += R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator-=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x -= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v -= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator*=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x *= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v *= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator/=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x /= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v /= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator%=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x %= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v %= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator&=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x &= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v &= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator|=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x |= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v |= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator^=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x ^= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v ^= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator<<=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x <<= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v <<= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator>>=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x >>= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v >>= R::value; }()>{}; }
};
Unlike other operators, `constant_wrapper`'s pseudo-mutators only require that the wrapped type has
corresponding mutators, but do not require them to be constexpr or to return a sensible value.
This inconsistency loses the SFINAE friendliness (demo):
#include <type_traits>
void test(auto t) {
if constexpr (requires { +t; }) // ok
+t;
if constexpr (requires { -t; }) // ok
-t;
if constexpr (requires { ++t; }) // hard error
++t;
if constexpr (requires { --t; }) // hard error
--t;
}
struct S {
/* constexpr */ int operator+() const { return 0; }
/* constexpr */ int operator++() { return 0; }
constexpr void operator-() const { }
constexpr void operator--() { }
};
int main() {
test(std::cw<S{}>);
}
Since these pseudo-mutators have constraints, it is reasonable to further require constant expressions.
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2025-11-11 10:48:55 | admin | set | messages: + msg15677 |
| 2025-11-11 10:48:55 | admin | set | status: immediate -> wp |
| 2025-11-08 02:59:13 | admin | set | messages: + msg15608 |
| 2025-11-08 02:59:13 | admin | set | status: new -> immediate |
| 2025-11-06 01:52:03 | admin | set | messages: + msg15541 |
| 2025-10-17 14:34:50 | admin | set | messages: + msg15229 |
| 2025-09-27 05:32:23 | admin | set | messages: + msg15084 |
| 2025-09-24 00:00:00 | admin | create | |