Rename std :: nontype ,
and make it broadly useful
- Document number:
- P3774R1
- Date:
2025-08-14 - 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 some acceptance in LEWG (but not formal consensus),
and LWG had no time to see the paper,
so it did not make it into C++26.
While R0 of this paper proposed more significant changes,
following decisions made during LEWG Telecon 2025-08-12,
we now simply propose to rename to ,
with much greater consensus,
which reaffirms the [P3740R1] decision.
Contents
Revision history
Changes since R0
Introduction
History up to Sofia 2025
Post-Sofia decisions
Why did we need a new std :: nontype type?
Refresher on what std :: nontype accomplishes
Formerly 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 — Formerly considered wording for C++29
1. Revision history
1.1. Changes since R0
-
implement LEWG decisions from LEWG Telecon 2025-08-12 in §6. Wording,
i.e. simply rename
tostd :: nontype std :: constant_arg - add disclaimers across the paper the clarify that certain changes are no longer proposed, but avoid "erasing history" or drastically altering the papers' contents
- summarize the LEWG Telecon decisions in §2.2. Post-Sofia decisions
- update the abstract to reflect these changes
2. Introduction
2.1. History up to Sofia 2025
[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 was most popular
(without actually constituting consensus, and without LWG seeing the paper),
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, as already stated, these polls did not actually constitute consensus, and LWG did not see the paper.
became the status quo,
and the last poll did not have enough votes to change that status quo.
2.2. Post-Sofia decisions
The paper was discussed during the LEWG 2025-08-12 Telecon;
See also [TeleconMinutes] and [P3775R0] (presentation slides).
Firstly, issues with using were discussed in great detail;
resulting in the following poll:
POLL: We want to explore replacing nontype with constant_wrapper instead of renaming.
SF F N A SA 2 1 3 7 4 Attendance: 20
Authors' opinion: 3xSA
Outcome: Consensus against
Next, it was decided to rename :
POLL: We want to rename
.std :: nontype
SF F N A SA 6 6 3 0 1 Attendance: 20
Authors' opinion: 3xSF
Outcome: Consensus in favor
Next, an in-depth discussion regarding the naming of ensued.
The preferred design of this paper
(rename to , add a call operator to the type in C++29)
did not find favor (under then name ).
That is in part because it would mean renaming the type in C++26,
on the conjecture that a change will happen in C++29 that justifies the choice of name.
POLL: We want to rename std::nontype/std::nontype_t to std::fw / std::function_wrapper.
SF F N A SA 5 3 2 1 3 Attendance: 20
Authors' opinion: 3xSF
Outcome: No consensus for change
Finally, the room decided to reaffirm the most popular option of Sofia 2025:
POLL: In P3774R0 change the name to std::constant_arg / std::constant_arg_t, without moving the facility to and forward the resulting revision to LWG for C++26 (to be confirmed by electronic poll).
SF F N A SA 8 4 1 0 1 Attendance: 19
Author's opinion: 3xSF
Outcome: consensus in favor
2.3. 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.4. 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. Formerly proposed solution
to ,
and adding a call operator to such a general-purpose type is not something
any of the authors want.
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.
3.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 .
3.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 .
3.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 §5.2. Possible implementation of for C++29 for specifics.
3.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.
3.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.
3.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.
4. 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.
4.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.
4.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 §2.4. 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.
4.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.
4.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.
5. Implementation experience
5.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.
5.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 — Formerly considered wording for C++29
6. Wording
The following changes are relative to [N5008].
In [version.syn], update the feature-test macro:
In [utility.syn],
replace every occurrence of with
and with , respectively.
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_t constant_arg_t
In [func.wrap.ref.deduct],
replace every occurrence of with :
[…]
[…]
[…]
7. References
Appendix — Formerly considered wording for C++29
The following changes are relative to [N5008], with the changes in §6. 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 > ;