diff --git a/source/meta.tex b/source/meta.tex index cb8e6e7ade..58671c96a9 100644 --- a/source/meta.tex +++ b/source/meta.tex @@ -164,6 +164,20 @@ using @\libglobal{true_type}@ = bool_constant; using @\libglobal{false_type}@ = bool_constant; + template + struct @\exposid{cw-fixed-value}@; // \expos + + template<@\exposid{cw-fixed-value}@ X, class = typename decltype(@\exposid{cw-fixed-value}@(X))::type> + struct constant_wrapper; + + template + concept @\exposid{constexpr-param}@ = requires { typename constant_wrapper; }; // \expos + + struct @\exposid{cw-operators}@; // \expos + + template<@\exposid{cw-fixed-value}@ X> + constexpr auto cw = constant_wrapper{}; + // \ref{meta.unary.cat}, primary type categories template struct is_void; template struct is_null_pointer; @@ -624,6 +638,265 @@ are used as base classes to define the interface for various type traits. +\indexlibrarymember{value_type}{integral_constant}% +\rSec2[const.wrap.class]{Class template \tcode{constant_wrapper}} + +\begin{codeblock} +template +struct @\exposid{cw-fixed-value}@ { // \expos + using type = T; // \expos + constexpr @\exposid{cw-fixed-value}@(type v) noexcept: data(v) { } + T data; // \expos +}; + +template +struct @\exposid{cw-fixed-value}@ { // \expos + using type = T[Extent]; // \expos + constexpr @\exposid{cw-fixed-value}@(T (&arr)[Extent]) noexcept; + T data[Extent]; // \expos +}; + +template + @\exposid{cw-fixed-value}@(T (&)[Extent]) -> cw-fixed-value; // \expos + +struct @\exposid{cw-operators}@ { // \expos + // unary operators + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)> + { return {}; } + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator-(T) noexcept -> constant_wrapper<(-T::value)> + { return {}; } + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator~(T) noexcept -> constant_wrapper<(~T::value)> + { return {}; } + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator!(T) noexcept -> constant_wrapper<(!T::value)> + { return {}; } + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator&(T) noexcept -> constant_wrapper<(&T::value)> + { return {}; } + template<@\exposid{constexpr-param}@ T> + friend constexpr auto operator*(T) noexcept -> constant_wrapper<(*T::value)> + { return {}; } + + // binary operators + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)> + { return {}; } + + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)> + { return {}; } + + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + requires (!is_constructible_v || + !is_constructible_v) + friend constexpr auto operator&&(L, R) noexcept -> constant_wrapper<(L::value && R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + requires (!is_constructible_v || + !is_constructible_v) + friend constexpr auto operator||(L, R) noexcept -> constant_wrapper<(L::value || R::value)> + { return {}; } + + // comparisons + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator<=>(L, R) noexcept -> constant_wrapper<(L::value <=> R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator<(L, R) noexcept -> constant_wrapper<(L::value < R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator<=(L, R) noexcept -> constant_wrapper<(L::value <= R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator==(L, R) noexcept -> constant_wrapper<(L::value == R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator!=(L, R) noexcept -> constant_wrapper<(L::value != R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator>(L, R) noexcept -> constant_wrapper<(L::value > R::value)> + { return {}; } + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator>=(L, R) noexcept -> constant_wrapper<(L::value >= R::value)> + { return {}; } + + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator,(L, R) noexcept = delete; + template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R> + friend constexpr auto operator->*(L, R) noexcept -> constant_wrapper*(R::value)> + { return {}; } + + // call and index + template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@... Args> + constexpr auto operator()(this T, Args...) noexcept + requires requires(Args...) { constant_wrapper(); } + { return constant_wrapper{}; } + template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@... Args> + constexpr auto operator[](this T, Args...) noexcept + -> constant_wrapper<(T::value[Args::value...])> + { return {}; } + + // pseudo-mutators + template<@\exposid{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<@\exposid{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<@\exposid{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<@\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{constexpr-param}@ T, @\exposid{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<@\exposid{cw-fixed-value}@ X, class> +struct constant_wrapper: cw-operators { + static constexpr const auto & value = X.data; + using type = constant_wrapper; + using value_type = typename decltype(X)::type; + + template<@\exposid{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; }()>{}; } + + constexpr operator decltype(auto)() const noexcept { return value; } +}; +\end{codeblock} + +\pnum +The class template \tcode{constant_wrapper} aids in metaprogramming by ensuring +that the evaluation of expressions comprised entirely of \tcode{constant_wrapper} +are core constant expressions\iref{expr.const}, +regardless of the context in which they appear. +In particular, this enables use of \tcode{constant_wrapper} values +that are passed as arguments to constexpr functions to be used in constant expressions. + +\pnum +\begin{note} +The unnamed second template parameter to \tcode{constant_wrapper} is present +to aid argument-dependent lookup\iref{basic.lookup.argdep} +in finding overloads for which \tcode{constant_wrapper}'s wrapped value is a suitable argument, +but for which the \tcode{constant_wrapper} itself is not. +\end{note} + +\pnum +\begin{example} +\begin{codeblock} + constexpr auto initial_phase(auto quantity_1, auto quantity_2) { + return quantity_1 + quantity_2; + } + + constexpr auto middle_phase(auto tbd) { + return tbd; + } + + void final_phase(auto gathered, auto available) { + if constexpr (gathered == available) + std::cout << "Profit!\n"; + } + + void impeccable_underground_planning() { + auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, std::cw<13>)); + static_assert(gathered_quantity == 55); + auto all_available = std::cw<55>; + final_phase(gathered_quantity, all_available); + } + + void deeply_flawed_underground_planning() { + constexpr auto gathered_quantity = middle_phase(initial_phase(42, 13)); + constexpr auto all_available = 55; + final_phase(gathered_quantity, all_available); // error: + // `gathered == available' is not a constant expression + } +\end{codeblock} +\end{example} + +\begin{itemdecl} +constexpr @\exposid{cw-fixed-value}@(T (&arr)[Extent]) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initialize elements of \tcode{data} with corresponding elements of \tcode{arr}. +\end{itemdescr} + \rSec2[meta.unary]{Unary type traits} \rSec3[meta.unary.general]{General}