Rename std :: nontype
,
and make it broadly useful
- Document number:
- P3774R0
- Date:
2025-07-15 - 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>,
Tomasz Kamiński <tomaszkam@gmail.com> - GitHub Issue:
- wg21.link/P3774/github
- Source:
- github.com/Eisenwave/cpp-proposals/blob/master/src/nontype-again.cow
to
,
now that the term "non-type template parameter" no longer exists in the C++26 standard.
This solution found weak consensus in LEWG,
but there was no time in LWG to review the change,
so it did not make it into C++26.
We now propose to rename it to
in C++26.
We also propose to make it more broadly useful in C++29 by adding further members.
Contents
Introduction
Why did we need a new std :: nontype
type?
Refresher on what std :: nontype
accomplishes
Proposed solution
Impact on std :: function_ref
Making std :: nontype
useful for use in algorithms and function wrappers
Making std :: nontype
useful for producing function pointers
Naming
Change of header
Interoperability with third-party function_ref
s
Alternatives 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
Implementation experience
Simple rename for C++26
Possible implementation of std :: fn
for C++29
Wording
References
Appendix — Follow-up wording for C++29
1. 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.
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".
,
and the corresponding variable template is called
.
This document refers to the feature as a whole as
.
Furthermore, the
from [P2781R9]
was accepted into C++26, and it seems like a good replacement for
at first glance.
At Sofia 2025, the choice to rename
to
instead of replacing it with
achieved consensus,
by the narrowest of margins
(see P3740 minutes):
ACTION: Get feedback on the decision from standard library implementers.
POLL: Forward “P3740R1: Last chance to fix std::nontype” selecting Option A (change from nontype to constant_wrapper, also add overloads to other function wrappers (std::function)) to LWG for C++26 (if possible)
SF F N A SA 0 4 0 7 8 Outcome: No consensus for change
POLL: Forward “P3740R1: Last chance to fix std::nontype” selecting Option A (change from nontype to constant_wrapper, DO NOT add overloads to other function wrappers (std::function)) to LWG for C++26 (if possible)
SF F N A SA 6 7 2 6 0 Outcome: Weak consensus in favor
POLL: Forward “P3740R1: Last chance to fix std::nontype” selecting Option B (rename “std::nontype” to “std::constant_arg”) to LWG for C++26 (if possible)
SF F N A SA 7 9 2 2 1 Outcome: Consensus in favor
POLL: Forward “P3740R1: Last chance to fix std::nontype” selecting Option A (change from nontype to constant_wrapper, DO NOT add overloads to other function wrappers (std::function)) instead of Option B (rename `std::nontype` to `std::constant_arg`) to LWG for C++26 (if possible)
SF F N A SA 9 4 2 4 4 Outcome: No consensus for change
However, LWG had no time to review this extremely last-minute change, so it did not make it into C++26.
became the status quo,
and the last poll did not have enough votes to change that status quo.
1.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
1.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.
2. Proposed solution
We propose to rename
and
to
and
.
Such a rename is well within the scope of an NB comment,
so it can be applied to C++26.
Furthermore, we make the type more broadly useful for C++29 by
- making
convertible to function pointers, andstd :: fn_t -
giving it a call operator, which makes it usable in function wrappers
other than
(with the same effect), as well as algorithms.std :: function_ref
Expressed in code, this would look as follows:
Among other reasons, keeping
instead of using
in its place was controversial because there was not enough motivation to keep both.
This is mainly because
provides no distinct functionality;
it is effectively
without all of the extra operator overloads.
What we propose addresses this problem.
2.1. Impact on std :: function_ref
would handle
exactly the same as
,
whether it has this C++29 functionality or not.
No C++26 code would be broken by adding those new members later.
That is because there are special constructors
for
taking
,
and these always win in overload resolution,
whether
has a call operator or other members, or not.
Furthermore,
needs to special-case
to provide the
and
constructors.
That is because only
provides the guarantee that its address
is irrelevant to
, and that it is empty.
Thanks to that guarantee,
can use the
pointer to refer to some user-specified entity,
or simply make it
.
2.2. Making std :: nontype
useful for use in algorithms and function wrappers
A common recommendation when using standard library algorithms
is to avoid the use of function pointers.
That is because all invocations of of say,
with function pointers of the same type
also result in identical instantiations.
Therefore, rather than making easily inlined direct calls to lambda
s,
makes indirect calls through a function pointer,
and unless the algorithm is inlined and the function pointer is constant-folded,
this may result in degraded performance.
The C++29
solves this problem by "elevating"
the function pointer into the type system:
is not intended to address this use case.
The goal of
is to accept
other
s in its operator overloads,
and to return
s from them,
so that computations are performed entirely within the type system.
On the contrary,
would simply invoke the callable type it wraps,
forwarding arguments to it, and return the result of that invocation.
Furthermore, C++29
with a call operator could provide the same functionality
to
,
,
and
;
it would simply be invocable like function pointers or lambdas.
Even without any constructors added to the standard wording,
implementations are free to apply small-object optimization,
i.e. no allocations are necessary,
which may improve quality of implementation further.
This optimization is simply part of the "as-if rule".
A
with a call operator just works
with all function wrappers and algorithms,
exactly as expected,
and with the same semantics as it has in
.
2.3. Making std :: nontype
useful for producing function pointers
Since the C++29
would be convertible to a function pointer,
it could also be used for producing and converting function pointers.
would be convertible to a function pointer type,
possibly with conversions of parameters and the returned value being applied,
not just if there is an "exact match" in signature.
is wrapped in
,
which is convertible to
, not just
,
because
is convertible to
.
This may be useful for passing function pointers to C APIs when on the C++ side, there is a slight mismatch between the C++ function signature and the expected function pointer type.
Similarly, C++29
could be used to produce function pointers
to arbitrary callable types such as lambdas,
s with a call operator, etc.
All of this is feasible by creating a function "on the fly"
within a conversion operator template.
See §4.2. Possible implementation of
for C++29 for specifics.
2.4. Naming
While using such a short and valuable name as
would make little sense if
was simply being renamed with no further plans,
we have big plans.
would be the idiomatic way of lifting function pointers into
the type system.
is inspired by the
and
function templates.
The future C++29
would have similar purpose to
;
it just wouldn't negate the result of an invocation.
However, such a short and valuable name is likely controversial.
A plausible alternative is to use
and
, analogous to
and
,
or
and
, analogous to
.
possibly being used as a namespace
for functional programming in the future.
However, the standard library generally has verbose namespaces (e.g.
),
and suggestion to add shorthands (e.g.
) were historically rejected.
2.5. Change of header
Consider that
is a more "functional name"
than
,
and that the goal is to follow up the rename with a call operator added in C++29,
is a more appropriate header than
.
Since
is located in
anyway,
this change is largely inconsequential.
It just means that users wouldn't be able to obtain
through
on its own,
but we don't see strong motivation to do so.
2.6. Interoperability with third-party function_ref
s
It is worth noting that the proposed C++29
would work out-of-the-box with third-party
implementations
such as
.
Notably,
would be a variable template,
meaning objects of type
have static storage duration.
Therefore, binding an
to
does not risk creating a dangling reference.
However, it is still possible to produce a dangling reference to
by deliberately copying it and creating a temporary object:
This is also one of the reasons why
should special-case
in its set of constructors.
Other function wrappers need no special cases because they take ownership
over the wrapped callable anyway.
3. Alternatives 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
[P3740R0] proposed to replace
with a set of constructors that take a
constructor tag.
This idea essentially died the moment LEWG saw the Tony Table
which compares how users would pass function pointers to
with
tags compared to
.
This alternative was not polled, but it was obvious during discussion that this option would not achieve consensus. See [P3740R0] for details.
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.
As demonstrated in §1.2. Refresher on what
accomplishes,
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
This functionality is important to
,
and it has shipped in C++26 already.
None of the authors see it as reasonable to rip this functionality out entirely for C++26.
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 been abandoned by the original author,
but picked up by a new author (proposal pending).
Nonetheless,
function parameters do not entirely obsolete our design,
it is unclear if they will be in C++29, and
the
functionality has already shipped in C++26.
Therefore, it makes very little sense to wait for this feature.
3.4. Replacing std :: nontype
with std :: constant_wrapper
The seemingly obvious solution is to use
from [P2781R9].
The option to use
as a replacement for
was discussed during an LEWG telecon 2025-03-11.
In short, the problems stem from the fact that
is already invocable; it already has a call operator,
whose semantics would be ignored by
,
in a way that is inconsistent with how
behaves anywhere else.
More details can be found in [P3740R1],
as well as in [P3792R0]
"Why
is not a usable replacement for
".
In any case, LEWG already decided (with paper-thin consensus) not to pursue this path.
4. Implementation experience
4.1. Simple rename for C++26
Renaming
requires no implementation experience
because it is merely changing the name of a symbol;
it obviously works.
This is what we are proposing for C++26.
4.2. Possible implementation of std :: fn
for C++29
An implementation for illustration purposes of the full C++29
may look as follows:
Note that the wording strategy for
in C++29
would leave its details largely unspecified,
which would allow for not implementing
at all,
and for making
invocable via surrogate function call
(implicit conversion to function pointer, then calling that pointer).
See § Appendix — Follow-up wording for C++29
5. Wording
The following changes are relative to [N5008].
In [version.syn], update the feature-test macro:
In [utility.syn],
delete the declarations of
and
:
In [functional.syn], change the synopsis as follows:
not_fn
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_tfn_t.
In [func.wrap.ref.deduct],
replace every occurrence of
with
:
[…]
[…]
[…]
6. References
Appendix — Follow-up wording for C++29
The following changes are relative to [N5008], with the changes in §5. Wording applied.
In [version.syn], create a feature-test macro:
In [functional.syn],
change the declaration of
and
as follows:
Between [func.identity] and [func.not.fn], insert a new subclause:
Constant function wrapper [func.fn]
1
Let
be an object of type
that is a (possibly const) specialization of
,
and let
be a template parameter object ([temp.param])
corresponding to the constant template argument of
.
Then:
-
is a trivially copyable type, such thatFT
modelsFT
andsemiregular
isis_empty_v < FT >
;true -
is a simple call wrapper ([func.require]) with no state entities and with the call patternft
, whereinvoke ( cf , call_args ... )
is an argument pack used in a function call expression ([expr.call]), except that any parameter of the function selected by overload resolution may be initialized from the corresponding element ofcall_args
if that element is a prvalue;call_args -
for any type
and pack of typesR
, bothArgs
andcf
are convertible to:std :: move ( ft )
ifR ( * ) ( Args ... )
isis_invocable_r_v < R , decltype ( cf ) , Args ... >
;true
ifR ( * ) ( Args ... ) noexcept
isis_nothrow_invocable_r_v < R , decltype ( cf ) , Args ...
.true
[Example:
— end example]
bool is_even ( long ) ;
bool ( * ptr ) ( int ) = std :: : fn < & is_even > ;