Date: Sun, 31 Aug 2025 17:22:34 -0400
On Sun, Aug 31, 2025 at 4:37 PM Ville Voutilainen <
ville.voutilainen_at_[hidden]> wrote:
> On Sun, 31 Aug 2025 at 23:23, Charles R Hogg via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >> In current C++ there are basically three ways to pass in a compile-time
> constant:
> >>
> >> as a template argument (in angle brackets),
> >> using an ordinary function argument whose type encodes the compile-time
> constant (for which one or more corresponding template arguments will be
> deduced), or
> >> using the `std::format` trick.
> >
> > This was really intriguing. I'm very familiar with the first two, but I
> don't know what "the `std::format` trick" refers to, and naive Googling
> didn't show me a good reference. Can you please either explain more, or
> else link to a reference that explains what you mean?
>
> It wraps a format-string to a consteval-only-constructible type. So it
> doesn't wrap a constant into a type, but it wraps a constant into an
> always-constant
> wrapper value. It boils down to the equivalent of
>
> class X
> {
> int val;
> public:
> consteval X(int v) : val(v) {}
> constexpr int get() {return val;}
> };
>
> int runtime() {return 666;}
>
> void other_runtime(X x) {
> int foo = x.get(); // no problem here
> }
>
> int main()
> {
> X x{42}; // OK
> //X x2{runtime()}; // can't construct X from a runtime value
> other_runtime(x); // this is the equivalent of the "std::format trick"
> }
>
Thanks! That's getting me into the right ballpark, but I'm still not 100%
"there" yet. Mainly, I don't get what we can actually "do" with the
constant.
I can see that if we do this, then a given *individual invocation* of
`other_runtime()` will always have the same value wrapped within `x`. But
it seems like that wrapped value will still essentially be a "runtime"
value, as far as the compiler is concerned. For example, we can't make the
single line inside of `other_runtime` to be `constexpr`. And this makes
sense, because it would be different for *different* invocations.
If I think about the `std::format` use case, I would expect a variadic
template parameter list to go after the `X`. In `std::format`, we're
checking whether the wrapped string is a valid format string for the types
of the args. So, still keeping it simple in *our* case, let's say that the
meaning of `X` is something like an "argument counter". Maybe we want it
to produce a compiler error if the number of variadic args is different
from the (wrapped constant) stored int value.
*This* is the part I'm getting hung up on. If `x` wrapped `2` instead of
`42`, how could we write `other_function` (adding variadic args) such that
`other_function(x, 1, 2)` would compile, and `other_function(x, 1, 2, 3)`
would not? Or am I just misunderstanding what we're even trying to do
here...?
Thanks,
Chip
ville.voutilainen_at_[hidden]> wrote:
> On Sun, 31 Aug 2025 at 23:23, Charles R Hogg via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >> In current C++ there are basically three ways to pass in a compile-time
> constant:
> >>
> >> as a template argument (in angle brackets),
> >> using an ordinary function argument whose type encodes the compile-time
> constant (for which one or more corresponding template arguments will be
> deduced), or
> >> using the `std::format` trick.
> >
> > This was really intriguing. I'm very familiar with the first two, but I
> don't know what "the `std::format` trick" refers to, and naive Googling
> didn't show me a good reference. Can you please either explain more, or
> else link to a reference that explains what you mean?
>
> It wraps a format-string to a consteval-only-constructible type. So it
> doesn't wrap a constant into a type, but it wraps a constant into an
> always-constant
> wrapper value. It boils down to the equivalent of
>
> class X
> {
> int val;
> public:
> consteval X(int v) : val(v) {}
> constexpr int get() {return val;}
> };
>
> int runtime() {return 666;}
>
> void other_runtime(X x) {
> int foo = x.get(); // no problem here
> }
>
> int main()
> {
> X x{42}; // OK
> //X x2{runtime()}; // can't construct X from a runtime value
> other_runtime(x); // this is the equivalent of the "std::format trick"
> }
>
Thanks! That's getting me into the right ballpark, but I'm still not 100%
"there" yet. Mainly, I don't get what we can actually "do" with the
constant.
I can see that if we do this, then a given *individual invocation* of
`other_runtime()` will always have the same value wrapped within `x`. But
it seems like that wrapped value will still essentially be a "runtime"
value, as far as the compiler is concerned. For example, we can't make the
single line inside of `other_runtime` to be `constexpr`. And this makes
sense, because it would be different for *different* invocations.
If I think about the `std::format` use case, I would expect a variadic
template parameter list to go after the `X`. In `std::format`, we're
checking whether the wrapped string is a valid format string for the types
of the args. So, still keeping it simple in *our* case, let's say that the
meaning of `X` is something like an "argument counter". Maybe we want it
to produce a compiler error if the number of variadic args is different
from the (wrapped constant) stored int value.
*This* is the part I'm getting hung up on. If `x` wrapped `2` instead of
`42`, how could we write `other_function` (adding variadic args) such that
`other_function(x, 1, 2)` would compile, and `other_function(x, 1, 2, 3)`
would not? Or am I just misunderstanding what we're even trying to do
here...?
Thanks,
Chip
Received on 2025-08-31 21:22:46