Skip to content

Commit b3d2eca

Browse files
committed
P2781R9 std::constant_wrapper
Fixes 7969 Fixes cplusplus/papers#1458 Allowed paragraphs to number themselves correctly. For consistency, did not use code font for constexpr in the phrase constexpr function. This revision has many Overfull hboxes that will be addressed next commit.
1 parent 691ab30 commit b3d2eca

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed

source/meta.tex

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,20 @@
164164
using @\libglobal{true_type}@ = bool_constant<true>;
165165
using @\libglobal{false_type}@ = bool_constant<false>;
166166

167+
template<class T>
168+
struct @\exposid{cw-fixed-value}@; // \expos
169+
170+
template<@\exposid{cw-fixed-value}@ X, class = typename decltype(@\exposid{cw-fixed-value}@(X))::type>
171+
struct constant_wrapper;
172+
173+
template<class T>
174+
concept @\exposid{constexpr-param}@ = requires { typename constant_wrapper<T::value>; }; // \expos
175+
176+
struct @\exposid{cw-operators}@; // \expos
177+
178+
template<@\exposid{cw-fixed-value}@ X>
179+
constexpr auto cw = constant_wrapper<X>{};
180+
167181
// \ref{meta.unary.cat}, primary type categories
168182
template<class T> struct is_void;
169183
template<class T> struct is_null_pointer;
@@ -624,6 +638,221 @@
624638
are used as base classes to define
625639
the interface for various type traits.
626640

641+
\indexlibrarymember{value_type}{integral_constant}%
642+
\rSec2[const.wrap.class]{Class template \tcode{constant_wrapper}}
643+
644+
\begin{codeblock}
645+
template<class T>
646+
struct @\exposid{cw-fixed-value}@ { // \expos
647+
using type = T; // \expos
648+
constexpr @\exposid{cw-fixed-value}@(type v) noexcept: data(v) { }
649+
T data; // \expos
650+
};
651+
652+
template<class T, size_t Extent>
653+
struct @\exposid{cw-fixed-value}@<T[Extent]> { // \expos
654+
using type = T[Extent]; // \expos
655+
constexpr @\exposid{cw-fixed-value}@(T (&arr)[Extent]) noexcept;
656+
T data[Extent]; // \expos
657+
};
658+
659+
template<class T, size_t Extent>
660+
cw-fixed-value(T (&)[Extent]) -> cw-fixed-value<T[Extent]>; // \expos
661+
662+
struct @\exposid{cw-operators}@ { // \expos
663+
// unary operators
664+
template<@\exposid{constexpr-param}@ T>
665+
friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)> { return {}; }
666+
template<@\exposid{constexpr-param}@ T>
667+
friend constexpr auto operator-(T) noexcept -> constant_wrapper<(-T::value)> { return {}; }
668+
template<@\exposid{constexpr-param}@ T>
669+
friend constexpr auto operator~(T) noexcept -> constant_wrapper<(~T::value)> { return {}; }
670+
template<@\exposid{constexpr-param}@ T>
671+
friend constexpr auto operator!(T) noexcept -> constant_wrapper<(!T::value)> { return {}; }
672+
template<@\exposid{constexpr-param}@ T>
673+
friend constexpr auto operator&(T) noexcept -> constant_wrapper<(&T::value)> { return {}; }
674+
template<@\exposid{constexpr-param}@ T>
675+
friend constexpr auto operator*(T) noexcept -> constant_wrapper<(*T::value)> { return {}; }
676+
677+
// binary operators
678+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
679+
friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)> { return {}; }
680+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
681+
friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)> { return {}; }
682+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
683+
friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)> { return {}; }
684+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
685+
friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)> { return {}; }
686+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
687+
friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)> { return {}; }
688+
689+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
690+
friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)> { return {}; }
691+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
692+
friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)> { return {}; }
693+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
694+
friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)> { return {}; }
695+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
696+
friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)> { return {}; }
697+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
698+
friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)> { return {}; }
699+
700+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
701+
requires (!is_constructible_v<bool, decltype(L::value)> || !is_constructible_v<bool, decltype(R::value)>)
702+
friend constexpr auto operator&&(L, R) noexcept -> constant_wrapper<(L::value && R::value)> { return {}; }
703+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
704+
requires (!is_constructible_v<bool, decltype(L::value)> || !is_constructible_v<bool, decltype(R::value)>)
705+
friend constexpr auto operator||(L, R) noexcept -> constant_wrapper<(L::value || R::value)> { return {}; }
706+
707+
// comparisons
708+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
709+
friend constexpr auto operator<=>(L, R) noexcept -> constant_wrapper<(L::value <=> R::value)> { return {}; }
710+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
711+
friend constexpr auto operator<(L, R) noexcept -> constant_wrapper<(L::value < R::value)> { return {}; }
712+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
713+
friend constexpr auto operator<=(L, R) noexcept -> constant_wrapper<(L::value <= R::value)> { return {}; }
714+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
715+
friend constexpr auto operator==(L, R) noexcept -> constant_wrapper<(L::value == R::value)> { return {}; }
716+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
717+
friend constexpr auto operator!=(L, R) noexcept -> constant_wrapper<(L::value != R::value)> { return {}; }
718+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
719+
friend constexpr auto operator>(L, R) noexcept -> constant_wrapper<(L::value > R::value)> { return {}; }
720+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
721+
friend constexpr auto operator>=(L, R) noexcept -> constant_wrapper<(L::value >= R::value)> { return {}; }
722+
723+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
724+
friend constexpr auto operator,(L, R) noexcept = delete;
725+
template<@\exposid{constexpr-param}@ L, @\exposid{constexpr-param}@ R>
726+
friend constexpr auto operator->*(L, R) noexcept -> constant_wrapper<L::value->*(R::value)>
727+
{ return {}; }
728+
729+
// call and index
730+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@... Args>
731+
constexpr auto operator()(this T, Args...) noexcept
732+
requires requires(Args...) { constant_wrapper<T::value(Args::value...)>(); }
733+
{ return constant_wrapper<T::value(Args::value...)>{}; }
734+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@... Args>
735+
constexpr auto operator[](this T, Args...) noexcept -> constant_wrapper<(T::value[Args::value...])>
736+
{ return {}; }
737+
738+
// pseudo-mutators
739+
template<@\exposid{constexpr-param}@ T>
740+
constexpr auto operator++(this T) noexcept requires requires(T::value_type x) { ++x; }
741+
{ return constant_wrapper<[] { auto c = T::value; return ++c; }()>{}; }
742+
template<@\exposid{constexpr-param}@ T>
743+
constexpr auto operator++(this T, int) noexcept requires requires(T::value_type x) { x++; }
744+
{ return constant_wrapper<[] { auto c = T::value; return c++; }()>{}; }
745+
746+
template<@\exposid{constexpr-param}@ T>
747+
constexpr auto operator--(this T) noexcept requires requires(T::value_type x) { --x; }
748+
{ return constant_wrapper<[] { auto c = T::value; return --c; }()>{}; }
749+
template<@\exposid{constexpr-param}@ T>
750+
constexpr auto operator--(this T, int) noexcept requires requires(T::value_type x) { x--; }
751+
{ return constant_wrapper<[] { auto c = T::value; return c--; }()>{}; }
752+
753+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
754+
constexpr auto operator+=(this T, R) noexcept requires requires(T::value_type x) { x += R::value; }
755+
{ return constant_wrapper<[] { auto v = T::value; return v += R::value; }()>{}; }
756+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
757+
constexpr auto operator-=(this T, R) noexcept requires requires(T::value_type x) { x -= R::value; }
758+
{ return constant_wrapper<[] { auto v = T::value; return v -= R::value; }()>{}; }
759+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
760+
constexpr auto operator*=(this T, R) noexcept requires requires(T::value_type x) { x *= R::value; }
761+
{ return constant_wrapper<[] { auto v = T::value; return v *= R::value; }()>{}; }
762+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
763+
constexpr auto operator/=(this T, R) noexcept requires requires(T::value_type x) { x /= R::value; }
764+
{ return constant_wrapper<[] { auto v = T::value; return v /= R::value; }()>{}; }
765+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
766+
constexpr auto operator%=(this T, R) noexcept requires requires(T::value_type x) { x %= R::value; }
767+
{ return constant_wrapper<[] { auto v = T::value; return v %= R::value; }()>{}; }
768+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
769+
constexpr auto operator&=(this T, R) noexcept requires requires(T::value_type x) { x &= R::value; }
770+
{ return constant_wrapper<[] { auto v = T::value; return v &= R::value; }()>{}; }
771+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
772+
constexpr auto operator|=(this T, R) noexcept requires requires(T::value_type x) { x |= R::value; }
773+
{ return constant_wrapper<[] { auto v = T::value; return v |= R::value; }()>{}; }
774+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
775+
constexpr auto operator^=(this T, R) noexcept requires requires(T::value_type x) { x ^= R::value; }
776+
{ return constant_wrapper<[] { auto v = T::value; return v ^= R::value; }()>{}; }
777+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
778+
constexpr auto operator<<=(this T, R) noexcept requires requires(T::value_type x) { x <<= R::value; }
779+
{ return constant_wrapper<[] { auto v = T::value; return v <<= R::value; }()>{}; }
780+
template<@\exposid{constexpr-param}@ T, @\exposid{constexpr-param}@ R>
781+
constexpr auto operator>>=(this T, R) noexcept requires requires(T::value_type x) { x >>= R::value; }
782+
{ return constant_wrapper<[] { auto v = T::value; return v >>= R::value; }()>{}; }
783+
};
784+
785+
template<cw-fixed-value X, class>
786+
struct constant_wrapper: cw-operators {
787+
static constexpr const auto & value = X.data;
788+
using type = constant_wrapper;
789+
using value_type = typename decltype(X)::type;
790+
791+
template<@\exposid{constexpr-param}@ R>
792+
constexpr auto operator=(R) const noexcept requires requires(value_type x) { x = R::value; }
793+
{ return constant_wrapper<[] { auto v = value; return v = R::value; }()>{}; }
794+
795+
constexpr operator decltype(auto)() const noexcept { return value; }
796+
};
797+
\end{codeblock}
798+
799+
\pnum
800+
The class template \tcode{constant_wrapper} aids in metaprogramming by ensuring
801+
that the evaluation of expressions comprised entirely of \tcode{constant_wrapper}
802+
are core constant expressions\iref{expr.const},
803+
regardless of the context in which they appear.
804+
In particular, this enables use of \tcode{constant_wrapper} values
805+
that are passed as arguments to constexpr functions to be used in constant expressions.
806+
807+
\pnum
808+
\begin{note}
809+
The unnamed second template parameter to \tcode{constant_wrapper} is present
810+
to aid argument-dependent lookup\iref{basic.lookup.argdep}
811+
in finding overloads for which \tcode{constant_wrapper}'s wrapped value is a suitable argument,
812+
but for which the \tcode{constant_wrapper} itself is not.
813+
\end{note}
814+
815+
\pnum
816+
\begin{example}
817+
\begin{codeblock}
818+
constexpr auto initial_phase(auto quantity_1, auto quantity_2) {
819+
return quantity_1 + quantity_2;
820+
}
821+
822+
constexpr auto middle_phase(auto tbd) {
823+
return tbd;
824+
}
825+
826+
void final_phase(auto gathered, auto available) {
827+
if constexpr (gathered == available)
828+
std::cout << "Profit!\n";
829+
}
830+
831+
void impeccable_underground_planning() {
832+
auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, std::cw<13>));
833+
static_assert(gathered_quantity == 55);
834+
auto all_available = std::cw<55>;
835+
final_phase(gathered_quantity, all_available);
836+
}
837+
838+
void deeply_flawed_underground_planning() {
839+
constexpr auto gathered_quantity = middle_phase(initial_phase(42, 13));
840+
constexpr auto all_available = 55;
841+
final_phase(gathered_quantity, all_available); // error: `gathered == available' is not a constant expression
842+
}
843+
\end{codeblock}
844+
\end{example}
845+
846+
\begin{itemdecl}
847+
constexpr @\exposid{cw-fixed-value}@(T (&arr)[Extent]) noexcept;
848+
\end{itemdecl}
849+
850+
\begin{itemdescr}
851+
\pnum
852+
\effects
853+
Initialize elements of \tcode{data} with corresponding elements of \tcode{arr}.
854+
\end{itemdescr}
855+
627856
\rSec2[meta.unary]{Unary type traits}
628857

629858
\rSec3[meta.unary.general]{General}

0 commit comments

Comments
 (0)