This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
constant_wrapper
's pseudo-mutators are underconstrained
Section: 21.3.5 [const.wrap.class] Status: New Submitter: Hewill Kang Opened: 2025-09-24 Last modified: 2025-09-27
Priority: Not Prioritized
View all issues with New status.
Discussion:
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.
Proposed resolution:
This wording is relative to N5014.
Modify 21.3.5 [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; }()>{}; } };