Skip to content

Commit 54ca64f

Browse files
AlisdairMtkoeppe
authored andcommitted
P2781R9 std::constant_wrapper
1 parent 558a03f commit 54ca64f

File tree

3 files changed

+274
-1
lines changed

3 files changed

+274
-1
lines changed

source/containers.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20190,7 +20190,7 @@
2019020190

2019120191
template<class T>
2019220192
concept @\defexposconcept{integral-constant-like}@ = // \expos
20193-
is_integral_v<decltype(T::value)> &&
20193+
is_integral_v<remove_cv_ref_t<decltype(T::value)>> &&
2019420194
!is_same_v<bool, remove_const_t<decltype(T::value)>> &&
2019520195
@\libconcept{convertible_to}@<T, decltype(T::value)> &&
2019620196
@\libconcept{equality_comparable_with}@<T, decltype(T::value)> &&

source/meta.tex

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

168+
template<class T>
169+
struct @\exposidnc{cw-fixed-value}@; // \expos
170+
171+
template<@\exposidnc{cw-fixed-value}@ X, class = typename decltype(@\exposid{cw-fixed-value}@(X))::type>
172+
struct constant_wrapper;
173+
174+
template<class T>
175+
concept @\defexposconceptnc{constexpr-param}@ = requires { typename constant_wrapper<T::value>; }; // \expos
176+
177+
struct @\exposidnc{cw-operators}@; // \expos
178+
179+
template<@\exposid{cw-fixed-value}@ X>
180+
constexpr auto @\libglobal{cw}@ = constant_wrapper<X>{};
181+
168182
// \ref{meta.unary.cat}, primary type categories
169183
template<class T> struct is_void;
170184
template<class T> struct is_null_pointer;
@@ -631,6 +645,264 @@
631645
are used as base classes to define
632646
the interface for various type traits.
633647

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

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

source/support.tex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
#define @\defnlibxname{cpp_lib_complex_udls}@ 201309L // also in \libheader{complex}
619619
#define @\defnlibxname{cpp_lib_concepts}@ 202207L
620620
// freestanding, also in \libheader{concepts}, \libheader{compare}
621+
#define @\defnlibxname{cpp_lib_constant_wrapper}@ 202506L // also in \libheader{type_traits}
621622
#define @\defnlibxname{cpp_lib_constexpr_algorithms}@ 202306L // also in \libheader{algorithm}, \libheader{utility}
622623
#define @\defnlibxname{cpp_lib_constexpr_atomic}@ 202411L // also in \libheader{atomic}
623624
#define @\defnlibxname{cpp_lib_constexpr_bitset}@ 202207L // also in \libheader{bitset}

0 commit comments

Comments
 (0)