Last chance to fix std :: nontype
- Document number:
- P3740R1
- Date:
2025-06-20 - Audience:
- LEWG
- Project:
- ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
- Reply-to:
- Jan Schultke <janschultke@gmail.com>
- Co-authors:
- Bronek Kozicki <brok@incorrekt.com>
- GitHub Issue:
- wg21.link/P3740/github
- Source:
- github.com/Eisenwave/cpp-proposals/blob/master/src/nontype.cow
constructor which utilize a helper type
.
This helper type may be obsoleted by other active proposals,
and its name has become a bad choice now that C++26 no longer replaced the term
"non-type template parameter",
with "constant template parameter".
Contents
Revision history
Changes since R0
Introduction
Why did we need a new std :: nontype
type?
Refresher on what std :: nontype
accomplishes
Options considered
Using a std :: stateless
constructor tag instead
Removing std :: nontype
with no replacement, revisiting for C++29
Waiting for constexpr
function parameters
Replacing std :: nontype
with std :: constant_wrapper
Exposing the BoundEntityType
Renaming std :: nontype
Conclusion
Implementation experience
Wording
Option A — Replacing std :: nontype
with std :: constant_wrapper
[version.syn]
[utility.syn]
[func.wrap.func] std :: function
[func.wrap.move] std :: move_only_function
[func.wrap.copy] std :: copyable_function
[func.wrap.ref] std :: function_ref
Option B — Renaming std :: nontype
to std :: constant_arg
References
1. Revision history
1.1. Changes since R0
-
abandoned the use of a
constructor tag, as it became apparent that this approach has little chance of finding consensusstd :: stateless -
instead investigated feasibility of using
instead ofstd :: constant_wrapper
, with proposed wording for that approachstd :: nontype
2. Introduction
[P2472R3] proposed additional overloads for the
constructor which utilize a helper type
.
This paper was merged into [P0792R14],
which was plenary-approved at Varna 2023 for C++26.
There is now a proposal [P2781R8] on track for C++29 which adds a
helper type,
and this could be a plausible replacement for
.
Considering that, do we even need
?
Furthermore, the naming choice
makes very little sense
following [P2841R1], which introduced concept and variable template parameters.
Since those are not types either, what the standard used to call
"non-type template parameter" has been renamed to "constant template parameter".
In short, we now have to decide for C++26 whether
should be renamed (and if so, what to),
whether it should be merged with
,
or whether these constructor overloads should be delayed until C++29.
,
and the corresponding variable template is called
.
This document refers to the feature as a whole as
.
2.1. Why did we need a new std :: nontype
type?
An obvious question may be why the existing
cannot be used instead.
This has multiple reasons:
requires specifying the type separately.std :: integral_constant < class T , T v >
has a lengthy name.std :: integral_constant
has a call operator which returnsstd :: integral_constant
, which produces friction with the other constructors.v
2.2. Refresher on what std :: nontype
accomplishes
is used only within constructors of
:
Intuitively,
is the C++ counterpart to the C idiom of passing
and a function pointer which that
is fed into,
as seen in
.
It is common practice to provide a null pointer to
and thus rely on a "capture-less" comparison.
With
,
can support such use "capture-less" uses:
Crucially, it would be impossible to create a
from a function directly without this helper.
At best, we could store function pointer within
Besides overhead, we need
constructors to make a
which has the functionality provided by a lambda,
but does not reference any specific lambda.
This is crucial for being able to store
somewhere long-term
without paying attention to the lifetime of the callable type it has been initialized with.
3. Options considered
Besides the proposed approach, there are other possible solutions. However, the author position is that every one of them is worse than what is proposed.
3.1. Using a std :: stateless
constructor tag instead
technique is less verbose at the call site,
which convinced multiple attendees that it would be the superior approach.
A more detailed discussion and proposed wording for this approach can be found in R0 of this paper.
We can ditch
and obtain the functionality it provides in a different way.
This would be very tempting because we don't have the dilemma of naming it right,
making it redundant with
,
having a weird workaround that becomes obsolete with
function parameters, etc.
Instead, we can get the same functionality using a tagged constructor.
The crucial observation in §2.2. Refresher on what
accomplishes is that the lambda we create for
,
,
, etc.
We don't need to hold an instance of these; we can just default-construct when needed.
Specifically, the tagged constructor would function like:
3.2. Removing std :: nontype
with no replacement, revisiting for C++29
Since there is a lot of active work in this area,
perhaps we could simply shove
into C++29 and deal with the problem later.
However, as demonstrated in §2.2. Refresher on what
accomplishes,
this helper type covers crucial use cases such as
- constructing
from a free function with no overhead, orstd :: function_ref - constructing
from an existing function pointer andstd :: function_ref
, in the style ofvoid *
.qsort
Author position:
The functionality provided by
is a crucial part of
,
not a nice-to-have feature that can be ripped out.
without these constructors would be akin
to using containers without move semantics.
3.3. Waiting for constexpr
function parameters
,
, and
are – to an extent – temporary hacks.
If we were able to write
… then the workaround of
would be obsolete.
At Kona 2023, EWG showed enthusiasm for this option
when discussing [P1045R1] "
Function parameters".
Poll: P2781R3 “std::constexpr_v” and P1045R1 “constexpr function parameters” EWG would like to solve th eproblem solved by std::constexpr_v in the language, for example as proposed by P1045R1 or with “expression aliases”, rather than solving it in library. An implementation is desired. C++26 seems ambitious.
SF F N A SA 6 8 5 1 0
However, [P1045R1] has not seen any seen any activity from the author since 2019.
As nice as a core language solution would be,
standard library features cannot be built on on hopes, dreams, and unimplemented hypotheticals.
Even if
function parameters were eventually implemented,
it may be possible to integrate them into the existing set of constructors
and to deprecate
.
Author position:
Overall, there is little reason to treat
function parameters
as a prerequisite; we don't need to wait.
3.4. Replacing std :: nontype
with std :: constant_wrapper
The seemingly obvious solution is to use
from [P2781R8].
The option to use
as a replacement for
was discussed during an LEWG telecon 2025-03-11.
Major concerns have been raised at the time,
but after gaining implementation experience,
these have been addressed.
In particular:
-
already has a call operator (which returns astd :: constant_wrapper
) and is convertible to its wrapped constant, so this was suspected to cause issues in the constructor overload set or otherwise. However, the other overloads takingstd :: constant_wrapper
(nowstd :: nontype
) would always win in overload resolution, so the extra functionality is inconsequential.std :: constant_wrapper -
could be used with other function wrappers (std :: constant_wrapper
etc.) in some cases already (see below), and its behavior would be inconsistent withstd :: move_only_function
. We address this by adding corresponding overloads to other function wrapper constructors.std :: function_ref
being treated specially
by
.
This works because the type of
has a call operator
which returns
in this case,
which is convertible to
.
Author position:
Considering that
is intended to wrap a constant,
much like
.
If this can be done with no major technical issues,
it presumably should be done.
However, if we do this, we have to add corresponding support for
for other function wrappers so that the use of
is not subtly different for different wrappers.
Also, this can only be done now;
adding support for that in C++29 risks altering the meaning of existing C++26 code which uses
.
3.5. Exposing the BoundEntityType
Remember the example in §2.2. Refresher on what
accomplishes.
Our end goal is to enable more possible constructions of
.
Technically, this could be done by ripping the implementation open and exposing its details:
However, this means that the user directly interacts with
,
,
but this may also be a
, it may be multiple pointers large, etc.
Author position:
Exposing the
3.6. Renaming std :: nontype
Perhaps the most simple solution is to simply rename
.
However, no good candidate with high consensus has been presented yet,
and many options would be awfully similar to
,
creating confusion:
-
seems almost like a "more official" alternative tostd :: constant
, when this type is extremely limited on scope.std :: constant_wrapper -
,std :: ctp
,std :: cta
and other abbreviations derived from "constant template argument/parameter" feel out-of-place in the standard library, and cannibalizestd :: ct
, the variable template forstd :: cw
.std :: constant_wrapper -
,std :: const_arg
,std :: constant_arg
and other such names describe the purpose of the type well, but are still reminiscent ofstd :: constexpr_arg
.std :: constant_wrapper
Author position:
Renaming
is not trivial,
and it's unlikely that any solution will make everyone happy.
Of all the options,
seems like the least bad candidate.
3.7. Conclusion
Every alternative seems bad in its own way,
but §3.4. Replacing
with
seems most obvious,
and §3.6. Renaming
seems least intrusive.
These are definitely something that we could do for C++26.
with
and for §3.6. Renaming
is presented.
4. Implementation experience
§3.6. Renaming
requires no implementation experience
because it is merely changing the name of a symbol.
§3.4. Replacing
with
was implemented at [GitHub1].
This was done by forking the existing reference implementation of [P0792R14].
This implementation already had
and
constructors taking
and has been thoroughly tested,
which made it easy to verify that
could be used instead.
A reference implementation for
was also tested for
integration in that repository.
§3.1. Using a
constructor tag instead has been implemented at [GitHub2].
This was done by forking the existing reference implementation of [P0792R14].
5. Wording
The following changes are relative to [N5008] with the changes in [P2781R8] applied.
5.1.
Option A — Replacing std :: nontype
with std :: constant_wrapper
5.1.1. [version.syn]
In [version.syn], update the feature-test macro:
5.1.2. [utility.syn]
In [utility.syn],
delete the declarations of
and
.
5.1.3. [func.wrap.func] std :: function
Change the synopsis in [func.wrap.func.general] as follows:
Make the following changes; specific wording to be provided in the next revision:
is currently used in
.
5.1.4. [func.wrap.move] std :: move_only_function
Change the synopsis in [func.wrap.func.general] as follows:
Make the following changes; specific wording to be provided in the next revision:
is currently used in
.
5.1.5. [func.wrap.copy] std :: copyable_function
Change [func.wrap.copy.class], as follows:
Make the following changes; specific wording to be provided in the next revision:
is currently used in
.
5.1.6. [func.wrap.ref] std :: function_ref
Change [func.wrap.ref.class], as follows:
Change [func.wrap.ref.ctor] as follows:
9
Let
be
.
10
Constraints:
is
.
11
Mandates:
If
is
,
then
is
.
12
Effects:
Initializes
with a pointer to an unspecified object or
null pointer value, and
with the address of a function
such that
is expression-equivalent ([defns.expression.equivalent]) to
.
13
Let
be
and
be
.
14 Constraints:
isis_rvalue_reference_v < U && >
, andfalse
isis-invocable-using < F , cv T & >
.true
15
Mandates:
If
17
Let
be
.
18
Constraints:
is
.
19
Mandates:
If
In [func.wrap.ref.deduct], change the deduction guides as follows:
2
Let
be
.
3
Constraints:
is
.
4
Remarks:
The deduced type is
.
5
Let
be
.
6 Constraints:
-
is of the formF
for a typeR ( G :: * ) ( A ... ) cv & opt noexcept ( E )
, orG -
is of the formF
for a typeM G :: *
and an object typeG
, in which case letM
beR
,invoke_result_t < F , T & >
be an empty pack, andA ...
beE
, orfalse -
is of the formF
for a typeR ( * ) ( G , A ... ) noexcept ( E )
.G
7
Remarks:
The deduced type is
.
5.2.
Option B — Renaming std :: nontype
to std :: constant_arg
In [version.syn], update the feature-test macro:
In [utility.syn], modify the header synopsis as follows:
In [func.wrap.ref.class],
replace every occurrence of
with
:
In [func.wrap.ref.ctor],
replace every occurrence of
with
:
[…]
[…]
[…]
[…]
21 Constraints:
is not the same type asT
,function_ref
isis_pointer_v < T >
, andfalse
is not a specialization ofT nontype_tconstant_arg_t.
In [func.wrap.ref.deduct],
replace every occurrence of
with
:
[…]
[…]
[…]