Skip to content

P2781R9 std::constant_wrapper #8018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 273 additions & 0 deletions source/meta.tex
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@
using @\libglobal{true_type}@ = bool_constant<true>;
using @\libglobal{false_type}@ = bool_constant<false>;

template<class T>
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<class T>
concept @\exposid{constexpr-param}@ = requires { typename constant_wrapper<T::value>; }; // \expos
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
concept @\exposid{constexpr-param}@ = requires { typename constant_wrapper<T::value>; }; // \expos
concept @\defexposconcept{constexpr-param}@ = requires { typename constant_wrapper<T::value>; }; // \expos


struct @\exposid{cw-operators}@; // \expos

template<@\exposid{cw-fixed-value}@ X>
constexpr auto cw = constant_wrapper<X>{};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constexpr auto cw = constant_wrapper<X>{};
constexpr auto @\libglobal{cw} = constant_wrapper<X>{};

… I think. I'm still new to indexing, myself. 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a missing @ at the end of libglobal{cw}. Otherwise agreed with the approach.


// \ref{meta.unary.cat}, primary type categories
template<class T> struct is_void;
template<class T> struct is_null_pointer;
Expand Down Expand Up @@ -624,6 +638,265 @@
are used as base classes to define
the interface for various type traits.

\indexlibrarymember{value_type}{integral_constant}%
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems to be a copy&paste left-over?

\rSec2[const.wrap.class]{Class template \tcode{constant_wrapper}}

\begin{codeblock}
template<class T>
struct @\exposid{cw-fixed-value}@ { // \expos
using type = T; // \expos
constexpr @\exposid{cw-fixed-value}@(type v) noexcept: data(v) { }
T data; // \expos
Comment on lines +647 to +649
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using type = T; // \expos
constexpr @\exposid{cw-fixed-value}@(type v) noexcept: data(v) { }
T data; // \expos
using @\exposid{type}@ = T; // \expos
constexpr @\exposid{cw-fixed-value}@(type v) noexcept: data(v) { }
T @\exposid{data}@; // \expos

At least, that's what I recall from review in LWG.

Copy link
Member

@jensmaurer jensmaurer Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are marked with "// \expos", so should use \exposid decoration, yes. (Also all uses of these identifiers.)

};

template<class T, size_t Extent>
struct @\exposid{cw-fixed-value}@<T[Extent]> { // \expos
using type = T[Extent]; // \expos
constexpr @\exposid{cw-fixed-value}@(T (&arr)[Extent]) noexcept;
T data[Extent]; // \expos
Comment on lines +654 to +656
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise.

};

template<class T, size_t Extent>
@\exposid{cw-fixed-value}@(T (&)[Extent]) -> cw-fixed-value<T[Extent]>; // \expos

struct @\exposid{cw-operators}@ { // \expos
// unary operators
template<@\exposid{constexpr-param}@ T>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
template<@\exposid{constexpr-param}@ T>
template<@\exposconcept{constexpr-param}@ T>

Forgot this one before. Same for all the other uses of constexpr-param

friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)>
friend constexpr auto @\libmember{operator+}{constant_wrapper}@(T) noexcept -> constant_wrapper<(+T::value)>

Same for all the other operators. Though, constant_wrapper isn't 100% correct, I believe it's what is most helpful even to implementers.

{ 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<bool, decltype(L::value)> ||
!is_constructible_v<bool, decltype(R::value)>)
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<bool, decltype(L::value)> ||
!is_constructible_v<bool, decltype(R::value)>)
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<L::value->*(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<T::value(Args::value...)>(); }
{ return constant_wrapper<T::value(Args::value...)>{}; }
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;
Comment on lines +828 to +830
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct constant_wrapper: cw-operators {
static constexpr const auto & value = X.data;
using type = constant_wrapper;
struct @\libglobal{constant_wrapper}@: cw-operators {
static constexpr const auto & @\libmember{value}{constant_wrapper}@ = X.data;
using @\libmember{type}{constant_wrapper}@ = constant_wrapper;

etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and make "data" \exposid, assuming it's the exposition-only thing from earlier.

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}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Initialize elements of \tcode{data} with corresponding elements of \tcode{arr}.
Initialize elements of \exposid{data} with corresponding elements of \tcode{arr}.

\end{itemdescr}

\rSec2[meta.unary]{Unary type traits}

\rSec3[meta.unary.general]{General}
Expand Down